0

私は仕事で以下の課題を抱えており、誰かが私を助けることができるかどうか疑問に思っています. 次のレコードに基づいてレコードを更新するには、カーソルを使用する必要があります。キーが 8 に等しくないときはいつでも、キーが 8 で始まるすべてのレコードをチェックし、その最大日付で date2 を更新する必要があります

私のデータは次のようになります。

╔══════════════════════════════════════╗
║     ID  date1      date2         key ║
╠══════════════════════════════════════╣
║     1   1/2/2014    5/2/2014      1  ║
║     1   5/2/2014    8/2/2014      8  ║
║     1   8/2/2014    9/2/2014      8  ║
║     1   11/2/2014   12/2/2014     1  ║   
║     1   12/2/2014   14/2/2014     8  ║ 
║     2   12/2/2014   14/2/2014     1  ║
║     2   14/2/2014   17/2/2014     8  ║
║     3   20/2/2014   23/2/2014     1  ║
╚══════════════════════════════════════╝

更新後、データは次のようになります。

╔══════════════════════════════════════╗
║     ID  date1      date2         key ║
╠══════════════════════════════════════╣
║     1   1/2/2014    9/2/2014      1  ║
║     1   5/2/2014    8/2/2014      8  ║
║     1   8/2/2014    9/2/2014      8  ║
║     1   11/2/2014   14/2/2014     1  ║   
║     1   12/2/2014   14/2/2014     8  ║ 
║     2   12/2/2014   17/2/2014     1  ║
║     2   14/2/2014   17/2/2014     8  ║
║     3   20/2/2014   23/2/2014     1  ║
╚══════════════════════════════════════╝
4

2 に答える 2

2

ID ごとに 1 つのキー 1 行があり、同じ ID 値を持つ既存のすべてのキー 8 行がそれに関連付けられている場合は、次の方法を試すことができます。

WITH maxdates AS (
  SELECT
    *,
    maxdate2 = MAX(CASE [key] WHEN 8 THEN date2 END) OVER (PARTITION BY ID)
  FROM dbo.atable
)
UPDATE maxdates
SET date2 = maxdate2
WHERE [key] = 1
  AND maxdate2 IS NOT NULL
;

これが仕組みです。maxdates共通テーブル式は、ウィンドウ MAX 関数を使用して、グループごと (この場合は ID ごと) の最大値を決定しますdate2。これが元のデータセットの場合:

╔═══════════════════════════════╗
║ ID  date1      date2      key ║
╠═══════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1   ║
║ 1   5/2/2014   8/2/2014   8   ║
║ 1   8/2/2014   9/2/2014   8   ║
║ 2   12/2/2014  14/2/2014  1   ║
║ 2   14/2/2014  17/2/2014  8   ║
║ 3   20/2/2014  23/2/2014  1   ║
╚═══════════════════════════════╝

CTE はそれを次のように変換します。

╔══════════════════════════════════════════╗
║ ID  date1      date2      key  maxdate2  ║
╠══════════════════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1    9/2/2014  ║
║ 1   5/2/2014   8/2/2014   8    9/2/2014  ║
║ 1   8/2/2014   9/2/2014   8    9/2/2014  ║
║ 2   12/2/2014  14/2/2014  1    17/2/2014 ║
║ 2   14/2/2014  17/2/2014  8    17/2/2014 ║
║ 3   20/2/2014  23/2/2014  1    NULL      ║
╚══════════════════════════════════════════╝

UPDATE ステートメントは最初に、更新されない行、つまりkeyが 8 の行と、関連付けられたキー 8 行を持たないキー 1 行をフィルターで除外し (これらは、不在によって決定されますmaxdate2)、次のサブセットになります。

╔══════════════════════════════════════════╗
║ ID  date1      date2      key  maxdate2  ║
╠══════════════════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1    9/2/2014  ║
║ 2   12/2/2014  14/2/2014  1    17/2/2014 ║
╚══════════════════════════════════════════╝

で更新date2maxdate2ます。

現在、ID ごとに複数のキー 1 行が許可されている場合でも、この方法を適用できます。同じ ID グループ内の関連する行のサブグループを認識するための別の基準を考え出す必要があります。つまり、最初にデータセットを次のように変換する必要があります。

╔═══════════════════════════════╗
║ ID  date1      date2      key ║
╠═══════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1   ║
║ 1   5/2/2014   8/2/2014   8   ║
║ 1   8/2/2014   9/2/2014   8   ║
║ 1   12/2/2014  14/2/2014  1   ║
║ 1   14/2/2014  17/2/2014  8   ║
║ 1   20/2/2014  23/2/2014  1   ║
╚═══════════════════════════════╝

このようなものに:

╔═════════════════════════════════════════╗
║ ID  date1      date2      key  rangeID  ║
╠═════════════════════════════════════════╣
║ 1   1/2/2014   5/2/2014   1    1        ║
║ 1   5/2/2014   8/2/2014   8    1        ║
║ 1   8/2/2014   9/2/2014   8    1        ║
║ 1   12/2/2014  14/2/2014  1    2        ║
║ 1   14/2/2014  17/2/2014  8    2        ║
║ 1   20/2/2014  23/2/2014  1    3        ║
╚═════════════════════════════════════════╝

次に、メソッドを適用します。

このような基準を追加する 1 つの方法は、次のクエリのように、条件付き実行カウントを使用することです。

WITH partitions AS (
  SELECT
    *,
    rangeID = COUNT(CASE [key] WHEN 1 THEN 1 END) OVER (PARTITION BY ID ORDER BY date1)
  FROM dbo.atable
),
maxdates AS (
  SELECT
    *,
    maxdate2 = MAX(CASE [key] WHEN 8 THEN date2 END) OVER (PARTITION BY ID, rangeID)
  FROM partitions
)
UPDATE maxdates
SET date2 = maxdate2
WHERE [key] = 1
  AND maxdate2 IS NOT NULL
;

基本的に、COUNT() OVER (... ORDER BY ...)これは実行中のカウントであり、CASE 式によって条件付きになります。カウントはキー 1 行でのみ増加し、他の行では同じままです。CTEはpartitions、ID パーティションごとに独立した実行カウントを取得します。その結果、前に示したように rangeID 値を取得します。

maxdatesCTEpartitionsは rangeID 値の結果を読み取り、先ほど説明した追加の基準として使用します。2 番目のクエリの残りの部分は、最初のクエリのロジックに従います。

この方法のライブ デモは、SQL Fiddle にあります。

参考になる関連マニュアルページ:

于 2014-07-06T22:54:32.947 に答える
0

は必要ありませんCURSOR

次のことをしようとしていますか?

(注 - 私は日付を dd/mm/yyyy として配置しました。これは他の惑星で行われる方法です)

DECLARE @MyData TABLE (ID INT, date1 DATE, date2 DATE, [key] INT)
INSERT INTO @MyData (ID, date1, date2, [key])
    SELECT 1, '2/1/2014',    '2/5/2014',  1 UNION ALL
    SELECT 1, '2/5/2014',    '2/8/2014',  8 UNION ALL
    SELECT 1, '2/8/2014',    '2/9/2014',  8 UNION ALL
    SELECT 2, '2/12/2014',   '2/14/2014', 1 UNION ALL
    SELECT 2, '2/14/2014',   '2/17/2014', 8 UNION ALL
    SELECT 3, '2/20/2014',   '2/23/2014', 1

UPDATE MD
SET MD.date2 = DT.MaxDate2
FROM @MyData    MD
JOIN 
(
    SELECT   ID
            ,MaxDate2   = MAX(Date2)
    FROM @MyData
    WHERE [key] = 8
    GROUP BY ID
)           DT  ON DT.ID = MD.ID
WHERE MD.[key] != 8

SELECT *
FROM @MyData
于 2014-07-06T19:25:54.090 に答える