2

約 3 億 5,000 万レコードのテーブルから結果を選択していますが、実行速度は非常に遅く、約 10 分です。犯人はORDER BYのようです。それを削除したかのように、クエリは一瞬しかかかりません。要点は次のとおりです。

SELECT TOP 100
    (columns snipped)
FROM (
    SELECT 
        CASE WHEN (e2.ID IS NULL) THEN
            CAST(0 AS BIT) ELSE CAST(1 AS BIT) END AS RecordExists,
        (columns snipped)
    FROM dbo.Files AS e1
    LEFT OUTER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
)  AS p1
ORDER BY p1.RecordExists

基本的に、Filesに対応するRecordがあるかどうかによって結果を並べ替えます。最初に処理する必要がないものとしてです。WHERE句を使用して 2 つのクエリを実行することもできますが、できれば 1 つのクエリで実行したいと考えています。

これをスピードアップする方法はありますか?

4

6 に答える 6

3

最終的な問題は、サブクエリでCASEを使用すると、仮の方法で使用されていないものにORDERBYが導入されることです。したがって、最初に中間結果セット全体を注文して、TOP100を見つける必要があります。これはすべて3億5000万以上のレコードです。2

この特定のケースでは、CASEを外部のSELECTに移動し、DESCの順序を使用して(現在のRecordExistsで「0」を意味するNULL値を最初に配置する)、トリック1を実行する必要があります。これは一般的なアプローチではありませんが、 Files.IDにインデックスが付けられている場合は、順序がはるかに高速になるはずです。(それでもクエリが遅い場合は、クエリプランを参照して、ORDER BYがインデックスを使用していない理由を確認してください。)

もう1つの方法は、ORDER BYのインデックスとして使用できるRecordExists(これもインデックス付けされている)の永続化された計算列を含めることです。

繰り返しになりますが、ORDER BYは、インデックス内を順番に読み取るだけで(外側の制限に一致する必要なレコード数まで)、3億5000万以上のレコードをオンザフライで注文する必要がない、引数可能なものに対して機能するという考え方です。 )。

SQL Serverは、サブクエリの中間結果セットが表示されるのを待つのではなく、この順序(および制限)をサブクエリにプッシュダウンできます。注文内容に基づいてクエリプランの違いを確認します。


1例:

SELECT TOP 100
    -- If needed
    CASE WHEN (p1.ID IS NULL) THEN
        CAST(0 AS BIT) ELSE CAST(1 AS BIT) END AS RecordExists,
    (columns snipped)
FROM (
    SELECT 
        (columns snipped)
    FROM dbo.Files AS e1
    LEFT OUTER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
)  AS p1
-- Hopefully ID is indexed, DESC makes NULLs (!RecordExists) go first
ORDER BY p1.ID DESC

2実際には、フルソートなしで最初の100 0の後で停止する可能性があるようです。少なくとも、閉じた関数範囲での極端なクエリプランナーの最適化では、中間結果で0がいつ検出されるかによって異なります。セット(最初の数千か、数億までか、まったくないか)。とにかく、SQLServerがこの極端なケースを説明しているとはとても思えません。つまり、このまだ引数を指定できない動作を当てにしないでください。

于 2012-09-20T22:44:11.710 に答える
2

このフォームを試してみてください

SELECT TOP(100) *
FROM (
    SELECT TOP(100)
        0 AS RecordExists
        --,(columns snipped)
    FROM dbo.Files AS e1
    WHERE NOT EXISTS (SELECT * FROM dbo.Records e2 WHERE e1.FID = e2.FID)
    ORDER BY SecondaryOrderColumn
) X
UNION ALL
SELECT * FROM (
    SELECT TOP(100)
        1 AS RecordExists
        --,(columns snipped)
    FROM dbo.Files AS e1
    INNER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
    ORDER BY SecondaryOrderColumn
) X
ORDER BY SecondaryOrderColumn

キーインデックス:
レコード(FID)
ファイル(FID、SecondaryOrdercolumn)

于 2012-09-20T22:43:19.913 に答える
1

非常に遅い理由は、order by 句のない非常に異なるクエリであるためです。

order by 句の場合: 3 億 5000 万行全体から一致するすべてのレコードを検索します。次に、それらを並べ替えます。

order by 句がない場合: 最初の 100 件の一致するレコードを検索します。止まる。

于 2012-09-20T22:41:22.003 に答える
0

Q:唯一の違いが「順序付け」の「あり/なし」であると言う場合、どういうわけか「トップ100」を内側の選択に移動できますか?

例:

SELECT
    (columns snipped)
FROM (
    SELECT TOP 100
        CASE WHEN (e2.ID IS NULL) THEN
            CAST(0 AS BIT) ELSE CAST(1 AS BIT) END AS RecordExists,
        (columns snipped)
    FROM dbo.Files AS e1
    LEFT OUTER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
)  AS p1
ORDER BY p1.RecordExists
于 2012-09-20T22:43:38.373 に答える
0

SQL Server では、null値はドメイン内のどの値よりも低く照合されます。次の 2 つのテーブルがあるとします。

create table dbo.foo
(
  id   int         not null identity(1,1) primary key clustered ,
  name varchar(32) not null unique nonclustered ,
)

insert dbo.foo ( name ) values ( 'alpha' )
insert dbo.foo ( name ) values ( 'bravo' )
insert dbo.foo ( name ) values ( 'charlie' )
insert dbo.foo ( name ) values ( 'delta' )
insert dbo.foo ( name ) values ( 'echo' )
insert dbo.foo ( name ) values ( 'foxtrot' )
go

create table dbo.bar
(
  id     int         not null identity(1,1) primary key clustered ,
  foo_id int             null foreign key references dbo.foo(id) ,
  name   varchar(32) not null unique nonclustered ,
)
go
insert dbo.bar( foo_id , name ) values( 1 , 'golf' )
insert dbo.bar( foo_id , name ) values( 5 , 'hotel' )
insert dbo.bar( foo_id , name ) values( 3 , 'india' )
insert dbo.bar( foo_id , name ) values( 5 , 'juliet' )
insert dbo.bar( foo_id , name ) values( 6 , 'kilo' )
go

クエリ

select *
from      dbo.foo foo
left join dbo.bar bar on bar.foo_id = foo.id
order by bar.foo_id, foo.id

次の結果セットが得られます。

id name    id   foo_id name
-- ------- ---- ------ -------
2  bravo   NULL NULL   NULL
4  delta   NULL NULL   NULL
1  alpha   1    1      golf
3  charlie 3    3      india
5  echo    2    5      hotel
5  echo    4    5      juliet
6  foxtrot 5    6      kilo

(7 row(s) affected)

これにより、クエリ オプティマイザー適切なインデックスを使用できるようになります (存在する場合)。ただし、そのようなインデックスが使用されることを保証するものではありません。

于 2012-09-20T23:12:39.617 に答える
0

これを試すことができますか?

SELECT TOP 100
    (columns snipped)
FROM dbo.Files AS e1
LEFT OUTER JOIN dbo.Records AS e2 ON e1.FID = e2.FID
ORDER BY e2.ID ASC

これにより、最初に e2.ID が null である場所がわかります。また、Records.ID がインデックス化されていることを確認してください。これにより、必要な順序が得られるはずです。

于 2012-09-20T23:38:59.087 に答える