web-dev-qa-db-ja.com

「行の目標」の異なる動作-なぜですか?

これは4億を超える行と16の非クラスター化インデックス、146列のヒープです。クラスタ化インデックスを作成できない理由に焦点を当てないでください。それは私の管理下にはなく、私の質問の範囲にもありません。

SQLサーバー:

Microsoft SQL Server 2012(SP4-GDR)(KB4057116)-11.0.7462.6(X64)Jan 5 2018 22:11:56 Copyright(c)Microsoft Corporation Enterprise Edition:Core-based Licensing(64-bit)on Windows NT 6.3(ビルド9600:)(ハイパーバイザー)データベース互換性レベル:SQL Server 2008(100)

次のクエリ(匿名化)を実行すると、 この実際の実行プランが取得されます

SELECT TOP ? *
FROM Schema1.Object1 Object2
WHERE Object2.Column1 LIKE ?
ORDER BY Object2.Column2 DESC

同じデータベースをSQL 2017 CU13に復元し、互換モードをSQL Server 2012(110)に変更します(以下は完全なバージョン名です)。私は この実際の実行計画 を取得します。

Microsoft SQL Server 2017(RTM-CU13)(KB4466404)-14.0.3048.4(X64)2018年11月30日12:57:58 Copyright(C)2017 Microsoft Corporation Enterprise Edition(64-bit)on Windows Server 2016 Standard 10.0(Build 14393 :)(ハイパーバイザー)

ここで「ROW GOAL」が活躍していることはよくわかりました。これは以前にクエリプランで公開されていなかったため、最初のプランでは表示できません。 Trace Flag 4138を使用してSQL Server 2008バージョンを実行し、DISABLE_OPTIMIZER_ROWGOALヒントを使用してSQL Server 2012バージョンを実行できることを確認します。どちらの場合も、パフォーマンスが大幅に向上します。オプティマイザが正しいインデックスを選択し、nested loop joinを選択しないため、ゲインが発生します。

質問:

  • 両方の互換性レベルがSQL Server 2014バージョンより前であるのに、なぜEstimatedRowsWithoutRowGoal(2008年には表示されないことを理解しています)が異なるのですか?
  • インデックススキャンがSQL 2008の110万行とSQL 2012の870万行を比較するのはなぜですか?どのヒューリスティックが変更されましたか(互換性120で起動された新しいオプティマイザーを使用していない場合でも)?

私は Paul Whiteの記事 と記事に記載されている8つのリンクすべてを読みました。また、 をご覧ください。Showplanのその他の機能強化– Pedro Lopesによる行の目標

2
SqlWorldWide

2つのクエリ(およびテーブル?)が完全に一致しません。

SQL Server 2008互換性レベルクエリには143列が返されますSQL Server 2012互換性レベルクエリには146列がありますが返されます。

これらの表の他の違いを確認してください。


インデックススキャンがSQL 2008の110万行とSQL 2012の870万行を比較するのはなぜですか?

インデックススキャンはObject2.Column2で並べ替えられた行を返し、ブックマークをプッシュします

(匿名化計画のブックマーク= column144/Column147

Object2.Column1でRIDルックアップとフィルタリングを行います。


最初の推測:ネクタイ?

これにより、Object2.Column2にはタイがあるため、最初に異なる行が読み取られると思います。

両方が順序付けられたプリフェッチを実行しています

  <NestedLoops Optimized="0" WithOrderedPrefetch="1">

...順序付けされたプリフェッチは、ネストされたループ結合の内側でキーの順序を保持するため、SQL Serverは非同期I/Oが正しい順序で処理されるようにします。 ...テストクエリには、オプティマイザが順序付きプリフェッチを使用してソートを回避できるORDER BY句が含まれています...

ソース

私は間違っているかもしれませんが、これから私が取るのは、順序が保持されている限り、異なる行が[〜#〜] rid [〜#〜]tieクレームをサポートするルックアップ演算子。

一意の順序でクエリを実行し、これらのクエリプランを質問に追加してみてください。


どちらの互換性レベルもSQL Server 2014バージョンより前であるのに、EstimatedRowsWithoutRowGoal(2008年には表示されないことを理解しています)が異なるのはなぜですか?

ここで詳細な統計情報が必要になります。統計はテーブル間で異なる推定値を示し、EstimatedRowsWithoutRowGoalに違いが生じます

同じ量の列を選択して両方のクエリを実行する前に、フルスキャン更新を実行してみてください。


テスト

SQL Serverインスタンス(2008)と(2017と互換性のある2012)の間でテストを行う場合

同じ量の行が返されます。

2008(互換性):

enter image description here

クエリプラン

2012(互換性):

enter image description here

クエリプラン

Column2を変更して多くの重複を保持する場合でも、見積もりと実績は同じままです。

UPDATE dbo.Object1
SET Object1.Column2
= Object1.Column2 % 400;

予想どおり、compat 2012 dbのテーブルに追加の列を追加しても、見積もりは同じままです

ALTER TABLE dbo.Object1 
ADD ExtraColumn1 int default(1) with values;
ALTER TABLE dbo.Object1 
ADD ExtraColumn2 int default(2) with values;
ALTER TABLE dbo.Object1 
ADD ExtraColumn3 int default(3) with values;

トレースフラグを有効にした同じストーリー

結論

この種のデータは、データのつながりに関する以前のステートメントを除外しますが、データセット(はるかに小さい)で問題を再現できないことも示しています。

column2全体を同じ値に設定しても、返される行の量に違いはありません。データの違いが原因のようです。


使用されるデータセット

CREATE DATABASE TEST
GO
USE [master]
GO
ALTER DATABASE [TEST] MODIFY FILE ( NAME = N'TEST', FILEGROWTH = 1048576KB )
GO
ALTER DATABASE [TEST] MODIFY FILE ( NAME = N'TEST_log', FILEGROWTH = 1048576KB )
GO
alter database test set recovery simple
GO
backup database test to disk = 'NUL'

GO

use TEST
go

CREATE TABLE dbo.Object1(
Column1 int,
Column2 int,
Column3 int,
Column4 int,
Column5 int,
Column6 int,
Column7 int,
Column8 int,
Column9 int,
Column10 int,
Column11 int,
Column12 int,
Column13 int,
Column14 int,
Column15 int,
Column16 int,
Column17 int,
Column18 int,
Column19 int,
Column20 int,
column1_varchar varchar(20));

CREATE INDEX Index1
on dbo.Object1(column15,Column16,Column17)
 CREATE INDEX Index2
on dbo.Object1(Column2,Column10,Column19)


SELECT top(1000000)  --1M
row_number() over(order by (select null)) as rownum
INTO #temp
FROM master..spt_values spt1
cross apply master..spt_values spt2
cross apply master..spt_values spt3


INSERT INTO dbo.Object1
WITH(TABLOCK)
SELECT 
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
rownum,
'ABC'+ CAST(rownum as varchar(20)) 
FROM #temp  
GO 3
4
Randi Vertongen

Randi Vertongenの回答は、最初の質問で提供した情報に基づいて期待できる最高のものです。他の読者のためにコメントすることはできないので、これは答えとしてのみ書いています。免責事項:ランディと私はこれについてチャットしました ここ

一度の情報は、あなたが従うのに役立ちます。 where句を含む上位100件の結果セットでは、すべての結果がcolumn2の値829000514900に属しています。

データセットは、2つの環境のヒープで完全に異なります。 column2に基づいてローリング合計を行うことにしました。

これが私が見つけたものです。小さな違いがまだ存在していると私は考えています[Forwarded Record][2]]がヒープにあります。

両方の環境でクエリの下を実行しました。

SELECT 
    column2, 
    COUNT(0) AS CountColumn2, 
    SUM (COUNT(column2)) OVER (ORDER BY column2 DESC) AS RunningTotal
FROM dbo.object1
GROUP BY column2 
ORDER BY column2 DESC

SQL Server 2008環境の結果:

読み込まれた行の実際の数= 1176085

colum2          CountColumn2    RunningTotal
829000515000    79              1175804
829000514900    159             1175963
829000514800    631             1176594

SQL Server 2012環境の結果:

読み取られた実際の行数= 8891415

colum2          CountColumn2            RunningTotal
829000515000    79                      8892962
829000514900    159                     8893121
829000514800    631                     8893752
1
SqlWorldWide