たとえば、sys動的ビューをヒットして特定のクエリプランを選択した場合、そのクエリプランを別のデータベースのプランキャッシュに挿入して、まったく同じクエリを実行できますか? (クエリがハッシュされ、新しいプランをいつ生成するかを決定するために比較されることを知っているので、私の例では、クエリが文字ごとにまったく同じであることを確認します。)
さて、それがあなたの質問への答えですが、おそらくなぜを知りたいでしょう。
データベースごとにデータが異なる可能性があるため、異なる計画が必要です。
WideWorldImporters
データベースを同じサーバーに復元するとします[〜#〜] twice [〜#〜](_WWI_1
_および_WWI_2
_としましょう)。 2つの同一のデータベースがあります。 SQL Serverは1つのプランを作成し、それを両方のデータベースのクエリで使用できます。
しかし問題は、これら2つのデータベースがオンラインになった瞬間に、「同じ」フォークが発生することです。それらは個別に変更できます。同じスキーマを維持していても、dataは独立して変更できます。したがって、SQL Serverは、計画をコンパイルするときにデータベースを個別に考慮する必要があります。 _WWI_1
_には、_WWI_2
_とは異なる statistics が含まれる可能性があり、異なるプランになる可能性があります。 SQL Serverが両方のデータベースに同じプランを使用するためには、SQL Serverはフォーク後の違いを追跡する必要があります。これは、別々のプランをコンパイル/維持するよりも複雑でコストがかかります。
あなたがソフトウェア会社であり、顧客のためのホストソフトウェアであるとしましょう。すべての顧客が独自のデータベースを取得します。ホスティング環境では、すべてのサーバーに数百のデータベースがあり、すべてのデータベースには同じスキーマがありますが、データは各クライアントに固有です。
1つのクライアントには、95%カリフォルニアのクライアントの顧客ベースがあります。カリフォルニアのすべての顧客の住所テーブルをクエリすると、テーブルscanが生成されます。
_-- For CustomerA this query returns 95% of the table, so it scans
SELECT CustomerID
FROM dbo.Addresses
WHERE StateCode = 'CA';
_
別のクライアントには、米国のすべての州に均等に分散された顧客ベースがあり、重要な国際ビジネスも持っています。比較的少数のクライアントがカリフォルニア出身です。この場合、カリフォルニアのすべての顧客の住所テーブルをクエリすると、テーブルseekが生成されます。
_-- For CustomerB this query returns <1% of the table, so it seeks
SELECT CustomerID
FROM dbo.Addresses
WHERE StateCode = 'CA';
_
統計が異なるために計画が根本的に異なる、同一のスキーマを持つデータベースに対する同一のクエリ。これら2つのデータベースが元々同じソースバックアップから復元された場合でも、SQL Serverは個別の計画をコンパイルする必要があります。
2つのクエリが100%同一であることは十分ではなく、スキーマは100%同一です。これらの基準のみに基づいて再利用されたプランは非常に間違っている可能性があります。そのため、SQL Serverはそれを行いません。
ええと…。
同じ「ソフトウェア会社であり、顧客のためのソフトウェアをホストする」の例を使用してみましょう。 SQL Serverの目的に関係なく、ホストされているすべてのデータベースで同じ計画をforceしたい。 計画ガイド を使用して、サーバー上のすべてのデータベースに同じガイダンスを適用できます。これはquite「insert [one] query plan into another database's plan cache」と同じではありません...しかし、事実上同じです。
SELECT COUNT(*) FROM dbo.SomeTable
のようなものは、2つのデータベースの両方で同じプランを使用するのに十分簡単でしょう。いいえ、それでもそうではありません!
例を作ってみましょう:
これを行うためのコードを次に示します。
_CREATE DATABASE Sample1;
GO
USE Sample1
GO
CREATE TABLE dbo.SomeTable (
SomeID int IDENTITY(1,1) PRIMARY KEY CLUSTERED,
StuffType int,
OtherStuff varchar(100),
INDEX OtherStuff(OtherStuff)
);
GO
SET NOCOUNT ON;
INSERT INTO dbo.SomeTable (StuffType,OtherStuff)
SELECT object_id%50,
name
FROM sys.objects;
GO 1000
BACKUP DATABASE Sample1 TO DISK = '/var/opt/mssql/data/Sample1.bak' WITH INIT;
RESTORE DATABASE Sample2 FROM DISK = '/var/opt/mssql/data/Sample1.bak'
WITH MOVE 'Sample1' TO '/var/opt/mssql/data/Sample2.mdf',
MOVE 'Sample1_log' TO '/var/opt/mssql/data/Sample2_log.ldf';
_
それでは、COUNT(*)
クエリを実行してみましょう。両方で同じ計画ですか?はい!私のラップトップでは、クラスター化されたPKをスキャンしてカウントを実行しています
SQL Serverは、PKが小さいため、PKのスキャンを選択しています。非クラスター化インデックスは非常に断片化されているため、大きくなります。
現在、インデックスのメンテナンスは_Sample1
_データベースで行われます(ただし、_Sample2
_では行われません)。誰かが_dbo.SomeTable
_のインデックスを再構築します:
_ALTER INDEX ALL ON Sample1.dbo.SomeTable REBUILD;
_
_Sample1
_では、非クラスター化インデックスはナイス&コンパクトになりました。 PKよりも小さいです。非クラスタ化インデックスのスキャンは、Sample1で適切な選択です。これは、インデックスが小さく、IOが少なく、高速であるためです。
_Sample2
_では、PKのスキャンが適切な選択です。これは、PKが小さく、IOが少なく、したがって高速であるためです。
2番目が最初の最新のバックアップから分岐された、同じデータを持つ同一のスキーマ。これらのクエリはどちらも実行プランが異なりますが、シナリオにはbestクエリプランもあります。