web-dev-qa-db-ja.com

サブクエリがあるときに、COUNT(OVER?)を使用してOFFSET&LIMITを作成する方法は?

頭を一周できないケースがあるようです。したがって、他の誰かにも役立つ可能性があるクエリへのポインタを見つけることを期待してここに来る。

以下では、結果を返す限り正しく機能するクエリがありますが、ここで示したものと同じであるがOFFSETがない2番目のクエリが必要で、出力はCOUNT(*)のみです。すべての行の。

私には2つの目的があります。

  1. COUNT(*)が同じクエリで返されるようにクエリを記述します。実際、私は優れた SQL SERVER – OFFSET/FETCH NEXT(ページング)から合計行数を取得する方法 などのヘルプピースをさまざまな方法で検討してきましたが、別のピースがあります。 ..
  2. クエリをテーブルにOVER(PARTITION BYINDEX SCANを生成するように見えるため、ウィンドウ関数(INDEX SEEKなど)またはよりパフォーマンスの高い方法で結合を書き換えます。実際のクエリはWHEREの部分がもう少し複雑ですが、クエリがもう少し単純でCOUNTMAXは、外部クエリと同時に使用できます。これでも勝利ですが、全体的なCOUNTを組み合わせることでさらに大きくなります。

多分私は現在噛んでいるよりも少しだけ噛んでいるのかもしれませんが、一方で、何かを学ぶ機会があるかもしれません。

ここにテーブルとデータがあります

CREATE TABLE Temp
(
    Id INT NOT NULL PRIMARY KEY,
    Created INT NOT NULL,
    ParentId INT,
    SomeInfo INT NOT NULL,
    GroupId INT NOT NULL

    CONSTRAINT FK_Temp FOREIGN KEY(ParentId) REFERENCES Temp(Id)
);

-- Some root levels nodes.
INSERT INTO Temp VALUES(1, 1, NULL, 1, 1);
INSERT INTO Temp VALUES(2, 2, NULL, 2, 2);
INSERT INTO Temp VALUES(3, 3, NULL, 1, 3);
INSERT INTO Temp VALUES(13, 13, NULL, 1, 1);

-- First order child nodes.
INSERT INTO Temp VALUES(4, 4, 1, 2, 1);
INSERT INTO Temp VALUES(5, 5, 2, 1, 2);
INSERT INTO Temp VALUES(6, 6, 3, 2, 3);

-- Second order child nodes.
INSERT INTO Temp VALUES(7, 7, 4, 1, 1);
INSERT INTO Temp VALUES(8, 8, 5, 2, 2);
INSERT INTO Temp VALUES(9, 9, 6, 1, 3);

SELECT
    Id,
    newestTable.SomeInfo,
    newestTable.Created,
    CASE WHEN newestTable.RootCount > 1 THEN 1 ELSE 0 END AS IsMulti
FROM
   Temp as originalTable
   INNER JOIN
   (
        SELECT
            SomeInfo,
            Max(Created) AS Created,
            Count(*) AS RootCount
        FROM
            Temp
        WHERE ParentId IS NULL AND GroupId = 1
        GROUP BY SomeInfo
    ) AS newestTable ON originalTable.SomeInfo = newestTable.SomeInfo AND originalTable.Created = newestTable.Created
/*WHERE
(
    originalTable.SomeInfo = 1
)*/
ORDER BY newestTable.Created ASC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY;

追伸また Postgresqlのサブクエリで使用される完全なテーブルでのグループ化を回避するために、サブクエリで外部制限オフセットとフィルターを適用する方法 は興味深いように見えます。

<編集:

のように見えます

SELECT
    Id,
    SomeInfo,
    GroupId,
    ParentId,
    MAX(Created) OVER(PARTITION BY SomeInfo) AS Created,
    COUNT(Id) OVER(PARTITION BY SomeInfo) AS RootCount,
    CASE WHEN COUNT(Id) OVER(PARTITION BY SomeInfo) > 1 THEN 1 ELSE 0 END AS IsMulti
FROM
    Temp
WHERE
(
    GroupId = 1 AND ParentId IS NULL
)
ORDER BY Created ASC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY;

そこに近づきます。しかし、問題は、2つの結果行が存在することです。これは、元のINNER JOINTempに結合して1行に間引いたためと思われます。ウィンドウ処理の前または後に何らかの方法で条件を適用して、元のクエリにより厳密に一致させる方法はあるのでしょうか。 (そして、これは明確に言うと同じクエリではありません。データが非常に少ないため、クエリは互いに近くにあるように見えます。)

4
Veksi

だから私にはあなたが欠けているのは「各インスタンスのトップCreatedレコードのみを返す」のようです。したがって、すべての行を取得していて、その最上位のCreated値が同じSomeInfoレコードに対するものである場合。残念ながら、MAX(Created) = Createdを基本のWHERE句に追加することはできません。

全体をCTEでラップするだけの場合は、WHEREMAX(Created) = Createdを追加するだけで、探しているものを取得できます(私が思うにCTEはすべての答えです)。

WITH CTE (ID, SomeInfo, GroupID, ParentID, Created, MaxCreated, RootCount, IsMulti)
AS
(
    SELECT
        Id,
        SomeInfo,
        GroupId,
        ParentId,
        Created,
        MAX(Created) OVER(PARTITION BY SomeInfo) AS MaxCreated,
        COUNT(Id) OVER(PARTITION BY SomeInfo) AS RootCount,
        CASE WHEN COUNT(Id) OVER(PARTITION BY SomeInfo) > 1 THEN 1 ELSE 0 END AS IsMulti
    FROM
        Temp
)
SELECT ID, SomeInfo, GroupID, ParentID,  MaxCreated AS Created, RootCount, IsMulti
FROM CTE
WHERE
(
    GroupId = 1 
    AND ParentId IS NULL
    AND Created = MaxCreated
)
ORDER BY MaxCreated ASC
OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY;

私の簡単なテストでは、同じ実行プランがあり、追加の実行時間はかかりません(下記の実行プランを参照)。 (これは小さな結果セットなので、おそらくまだテストする必要があります。)

Execution Plan from Test

うまくいけば、それがあなたが探しているものの多くです。

2
Kirk Saunders