6

カーソルをループせずに、SQL Server 2005 テーブルからレコードを選択的に削除しようとしています。テーブルには多くのレコード (場合によっては > 500,000) を含めることができるため、ループが遅すぎます。

データ:

ID, UnitID, Day, Interval, Amount

1   100     10   21        9.345

2   100     10   22        9.367

3   200     11   21        4.150

4   300     11   21        4.350

5   300     11   22        4.734

6   300     11   23        5.106

7   400     13   21       10.257

8   400     13   22       10.428

キーは、ID、UnitID、Day、Interval です。

この例では、レコード 2、5、および 8 を削除したいと考えています。これらは既存のレコードに隣接しています (キーに基づく)。

注: レコード 6 は削除されません。これは、5 がなくなると隣接していないためです。

求めすぎですか?

4

5 に答える 5

4

パフォーマンスの詳細については、私のブログの次の記事を参照してください。


以下のクエリの主なアイデアは、間隔の連続範囲からすべての偶数行を削除する必要があるということです。

つまり、与えられた場合(unitId, Day)、次のようになりますintervals

1
2
3
4
6
7
8
9

、2 つの連続した範囲があります。

1
2
3
4

6
7
8
9

、すべての偶数行を削除する必要があります。

1
2 -- delete
3
4 -- delete

6
7 -- delete
8
9 -- delete

、次のようになります。

1
3
6
8

ここでの「偶数行」はROW_NUMBER()、「の偶数値」ではなく、「範囲ごとの偶数」を意味することに注意してくださいinterval

クエリは次のとおりです。

DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT)

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345)
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.345)
INSERT INTO @Table VALUES (3, 200, 11, 21, 9.345)
INSERT INTO @Table VALUES (4, 300, 11, 21, 9.345)
INSERT INTO @Table VALUES (5, 300, 11, 22, 9.345)
INSERT INTO @Table VALUES (6, 300, 11, 23, 9.345)
INSERT INTO @Table VALUES (7, 400, 13, 21, 9.345)
INSERT INTO @Table VALUES (8, 400, 13, 22, 9.345)
INSERT INTO @Table VALUES (9, 400, 13, 23, 9.345)
INSERT INTO @Table VALUES (10, 400, 13, 24, 9.345)
INSERT INTO @Table VALUES (11, 400, 13, 26, 9.345)
INSERT INTO @Table VALUES (12, 400, 13, 27, 9.345)
INSERT INTO @Table VALUES (13, 400, 13, 28, 9.345)
INSERT INTO @Table VALUES (14, 400, 13, 29, 9.345)

;WITH   rows AS
        (
        SELECT  *,
                ROW_NUMBER() OVER
                (
                PARTITION BY
                        (
                        SELECT  TOP 1 qi.id AS mint
                        FROM    @Table qi
                        WHERE   qi.unitid = qo.unitid
                                AND qi.[day] = qo.[day]
                                AND qi.interval <= qo.interval
                                AND NOT EXISTS
                                (
                                SELECT  NULL
                                FROM    @Table t
                                WHERE   t.unitid = qi.unitid
                                        AND t.[day] = qi.day
                                        AND t.interval = qi.interval - 1
                                )
                        ORDER BY
                                qi.interval DESC
                        )
                ORDER BY interval
                ) AS rnm
        FROM    @Table qo
        )
DELETE
FROM    rows
WHERE   rnm % 2 = 0

SELECT  *
FROM    @table

アップデート:

より効率的なクエリを次に示します。

DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT)

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345)
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.345)
INSERT INTO @Table VALUES (3, 200, 11, 21, 9.345)
INSERT INTO @Table VALUES (4, 300, 11, 21, 9.345)
INSERT INTO @Table VALUES (5, 300, 11, 22, 9.345)
INSERT INTO @Table VALUES (6, 300, 11, 23, 9.345)
INSERT INTO @Table VALUES (7, 400, 13, 21, 9.345)
INSERT INTO @Table VALUES (8, 400, 13, 22, 9.345)
INSERT INTO @Table VALUES (9, 400, 13, 23, 9.345)
INSERT INTO @Table VALUES (10, 400, 13, 24, 9.345)
INSERT INTO @Table VALUES (11, 400, 13, 26, 9.345)
INSERT INTO @Table VALUES (12, 400, 13, 27, 9.345)
INSERT INTO @Table VALUES (13, 400, 13, 28, 9.345)
INSERT INTO @Table VALUES (14, 400, 13, 29, 9.345)

;WITH    source AS
        (
        SELECT  *, ROW_NUMBER() OVER (PARTITION BY unitid, day ORDER BY interval) rn
        FROM    @Table
        ),
        rows AS
        (
        SELECT  *, ROW_NUMBER() OVER (PARTITION BY unitid, day, interval - rn ORDER BY interval) AS rnm
        FROM    source
        )
DELETE
FROM    rows
WHERE   rnm % 2 = 0

SELECT  *
FROM    @table
于 2009-08-03T15:37:31.073 に答える
1

あなたが求めていることが可能だとは思いませんが、近づくことはできるかもしれません。次のような自己結合を持つレコードを見つけることで、ほとんどそれを行うことができるようです:

SELECT t1.id
FROM
  table t1 JOIN table t2 ON (
    t1.unitid = t2.unitid AND
    t1.day = t2.day AND
    t1.interval = t2.interval - 1
  )

しかし問題は、id=6 も検出されることです。ただし、このデータから一時テーブルを作成すると、元のデータよりもはるかに小さい可能性があるため、カーソルを使用してスキャンする方がはるかに高速になります (id=6 問題を修正するため)。DELETE FROM table WHERE id IN (SELECT id FROM tmp_table)その後、行を削除するためにa を実行できます。

カーソルを使用して ID=6 の問題を修正する方法があるかもしれませんが、もしそうなら、私にはわかりません。

于 2009-08-03T15:14:41.610 に答える
0

Lievenは非常に近いです-それはテストセットでは機能しましたが、さらにいくつかのレコードを追加すると、いくつかを見逃し始めます。

奇数/偶数の基準を使用することはできません。データがどのように分類されるかはわかりません。

このデータを追加して再試行してください。

INSERT @Table VALUES (9,    100,     10,   23,        9.345)

INSERT @Table VALUES (10,   100,     10,   24,        9.367)

INSERT @Table VALUES (11,   100,     10,   25,        4.150)

INSERT @Table VALUES (12,   100,     10,   26,        4.350)

INSERT @Table VALUES (13,   300,     11,   25,        4.734)

INSERT @Table VALUES (14,   300,     11,   26,        5.106)

INSERT @Table VALUES (15,   300,     11,   27,       10.257)

INSERT @Table VALUES (16,   300,     11,   29,       10.428)
于 2009-08-03T16:04:39.347 に答える
0

カーソルの代わりにWHILE ステートメントがあります。これをテーブル変数と組み合わせることで、許容できるパフォーマンスの範囲内で同じことを実行できる可能性があります。

于 2009-08-03T15:18:52.320 に答える
0
DECLARE @Table TABLE (ID INT, UnitID INT, [Day] INT, Interval INT, Amount FLOAT)

INSERT INTO @Table VALUES (1, 100, 10, 21, 9.345)
INSERT INTO @Table VALUES (2, 100, 10, 22, 9.367)
INSERT INTO @Table VALUES (3, 200, 11, 21, 4.150)
INSERT INTO @Table VALUES (4, 300, 11, 21, 4.350)
INSERT INTO @Table VALUES (5, 300, 11, 22, 4.734)
INSERT INTO @Table VALUES (6, 300, 11, 23, 5.106)
INSERT INTO @Table VALUES (7, 400, 13, 21, 10.257)
INSERT INTO @Table VALUES (8, 400, 13, 22, 10.428)

DELETE FROM @Table
WHERE ID IN (
  SELECT t1.ID
  FROM @Table t1
       INNER JOIN @Table t2 
            ON  t2.UnitID = t1.UnitID 
                AND t2.Day = t1.Day 
                AND t2.Interval = t1.Interval - 1
       LEFT OUTER JOIN @Table t3 
            ON  t3.UnitID = t2.UnitID 
                AND t3.Day = t2.Day 
                AND t3.Interval = t2.Interval - 1
  WHERE t3.ID IS NULL)

SELECT * FROM @Table
于 2009-08-03T15:19:01.557 に答える