web-dev-qa-db-ja.com

絶対値の日付の差からのパーティションごとの上位2行

非正規化されたデータベースに防犯カメラの映像のデータベースがあります。複数の画像を撮影する複数のカメラがある場所があります。

場所+カメラ+画像capture_dateは、クラスター化された主キーであり、現在、テーブル上の唯一のインデックスです。キッカーが1台のカメラを検索している場合、SSMSから1ミリ秒未満、Webアプリケーションから約70ミリ秒かかります。私の現在のCTEソリューションは、台のカメラで約3分かかります。

ある場所にあるカメラの概要を説明するには、特定の日付(現在の日付など)に最も近い各カメラから2つの画像を選択する必要があります。このため、絶対値が必要です(検索日の前後の日付も同様に有効です)。したがって、最小のABS(@date -capture_date)で検索しています。

これが現在のコードです。動作しますが、SARGableではなく、非常に低速です。また、パーティションごとに数十万の画像が存在する可能性があるため、CTEのカメラごとに上位2行のみが必要です。

DECLARE @date datetime,
        @location varchar(4)
SET     @date ='2011-12-13 12:00:00'
SET     @location='CS01';
WITH CTE AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION  BY Camera ORDER BY  abs(datediff(second,@date, [capture_date]))) AS Ranking
    FROM rs_camera_pictures
    WHERE 
        location=@location)
SELECT * FROM CTE WHERE Ranking <= 2
1
Ben Brocka

これは単なる出発点であり、調整が必要になる可能性があります。

基本的に、これにより、指定した日付に最も近い4つのキャプチャ日付が取得されます(2つは後、2つは前)。

使用するものを選択するために、外側の選択にいくつかのロジックを追加する必要がありますが、すべてではなく4つのフィールドでDATEDIFFを実行します。

WITH CTE AS
(
SELECT camera, MIN(r1.capture_date) as 'After', MAX(r2.capture_date) as 'Before'
FROM rs_camera_pictures r1
INNER JOIN rs_camera_pictures r2
    ON r1.location = r2.location
    AND r1.camera = r2.camera
WHERE r1.capture_date > @date
AND r2.capture_date < @date
GROUP BY camera
UNION ALL
SELECT camera, MIN(r1.capture_date) as 'After', MAX(r2.capture_date) as 'Before'
FROM rs_camera_pictures r1
INNER JOIN rs_camera_pictures r2
    ON r1.location = r2.location
    AND r1.camera = r2.camera
INNER JOIN CTE c
    ON r1.location = c.location
    AND r1.camera = c.camera
WHERE r1.capture_date > c.[After]
AND r2.capture_date < c.Before
GROUP BY camera
)
1
JNK

これにより、選択した場所のカメラごとに4行が返され、それらをランク付けして、各カメラに最適な2行が返されます。

LocationCameraは、場所のカメラの個別のリストです。

DECLARE @date datetime,
        @location varchar(4)
SET     @date ='2011-12-13 12:00:00'
SET     @location='CS01';

SELECT      Location,Camera,Capture_Date,Ranking
FROM        (
                SELECT      Location,Camera,Capture_Date,
                            ROW_NUMBER() OVER (PARTITION  BY Camera ORDER BY  abs(datediff(second,@date, [capture_date]))) AS Ranking
                FROM        (
                                SELECT      L.Location,
                                            L.Camera,
                                            CP.capture_date
                                FROM        LocationCamera  L
                                CROSS APPLY (
                                                SELECT      TOP (2)
                                                            CP.[capture_date]
                                                FROM        rs_camera_pictures  CP
                                                WHERE       L.Location          = CP.Location
                                                AND         L.Camera            = CP.Camera
                                                AND         CP.capture_date     >= @date
                                                ORDER BY    [capture_date]
                                            )   TopN
                                WHERE       L.Location      = @Location
                                UNION ALL
                                SELECT      L.Location,
                                            L.Camera,
                                            CP.[capture_date]
                                FROM        LocationCamera  L
                                CROSS APPLY (
                                                SELECT      TOP (2)
                                                            CP.[capture_date]
                                                FROM        rs_camera_pictures  CP
                                                WHERE       L.Location          = CP.Location
                                                AND         L.Camera            = CP.Camera
                                                AND         CP.capture_date     < @date
                                                ORDER BY    [capture_date]      DESC
                                            )   BottomN
                                WHERE       L.Location      = @location
                            )   D
            )   R
WHERE       Ranking <= 2
1
MartinC

私の知る限り(さらに、私がまだ知らなかった方法があるかどうかを確認するためのGoogleの検索とハッキング)、非SARGable ABS(DATEDIFFを回避し、現在のパフォーマンスを上回る解決策はありません。

したがって、SARG不可能な検索を実装する必要がある場合、最も重要なことは、セットのサイズを制限するSARG可能な方法を見つけることです。現在、WHERE句にはSARGable句が含まれていますが、カメラごとに数十万行になる可能性がある場合(つまり、場所ごとに数百万行になる可能性がある場合)、問題のドメインをさらに縮小することは理にかなっています。

最大検索距離に適切な値を選択し、日付の上限と下限を設定することをお勧めします。例えばカメラに障害が発生していない限り、システムは通常、常にleast 5分ごとに写真を撮るとします。 6分の上限と下限を設定して(タイミングの「ジッター」を考慮して)、それらの範囲内の画像のみをランク付けできます。これは次のようになります。

DECLARE @date datetime,
        @location varchar(4) ,
        @dateUB DATETIME,
        @dateLB DATETIME
SET     @date ='2011-12-13 12:00:00'
SET     @DateUB = DATEADD(minute, 6, @matchDate)
SET     @DateLB = DATEADD(minute, -6, @matchDate)
SET     @location='CS01';
WITH CTE AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION  BY Camera ORDER BY  abs(datediff(second,@date, [capture_date]))) AS Ranking
    FROM rs_camera_pictures
    WHERE 
        location=@location
    AND capture_date BETWEEN @dateUB AND @dateLB )
SELECT * FROM CTE WHERE Ranking <= 2

Capture_dateとlocationがインデックスでカバーされている場合、これにより大幅な改善が得られるはずです。

1

たぶん、選択した日付から両方向にトップ2を取り、次にその日からトップ2を取ります。

DECLARE @date datetime,
        @location varchar(4)
SET     @date ='2011-12-13 12:00:00'
SET     @location='CS01';
WITH candidates AS (
    SELECT * FROM (
        SELECT TOP 2 *
        FROM rs_camera_pictures
        WHERE location=@location
            AND capture_date <= @date
        ORDER BY capture_date DESC
    ) [before]
    UNION
    SELECT * FROM (
        SELECT TOP 2 *
        FROM rs_camera_pictures
        WHERE location=@location
            AND capture_date >= @date
        ORDER BY capture_date ASC
    ) [after]
),
CTE AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION  BY Camera ORDER BY  abs(datediff(second,@date, [capture_date]))) AS Ranking
    FROM candidates
    WHERE 
        location=@location
)
SELECT * FROM CTE WHERE Ranking <= 2
0
db2