私はサロゲートキーのファンです。私の発見が確認バイアスのリスクがある。
こことhttp://stackoverflow.comの両方で見た多くの質問では、IDENTITY()
値に基づく代理キーの代わりに自然キーを使用しています。
コンピューターシステムの私のバックグラウンドは、整数の比較演算を実行すると、文字列を比較するよりも高速になることを教えてくれます。
このコメントは私の信念に疑問を投げかけたので、整数はSQL Serverのキーとして使用する文字列よりも高速であるという仮説を調査するシステムを作成すると思いました。
小さなデータセットでは識別可能な差はほとんどないため、プライマリテーブルに1,000,000行、セカンダリテーブルにプライマリテーブルの各行に10行、合計で10,000,000行の2つのテーブル設定を考えました。二次テーブル。私のテストの前提は、このような2つのテーブルセットを作成することです。1つは自然キーを使用し、もう1つは整数キーを使用し、次のような簡単なクエリでタイミングテストを実行します。
SELECT *
FROM Table1
INNER JOIN Table2 ON Table1.Key = Table2.Key;
以下は、テストベッドとして作成したコードです。
USE Master;
IF (SELECT COUNT(database_id) FROM sys.databases d WHERE d.name = 'NaturalKeyTest') = 1
BEGIN
ALTER DATABASE NaturalKeyTest SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE NaturalKeyTest;
END
GO
CREATE DATABASE NaturalKeyTest
ON (NAME = 'NaturalKeyTest', FILENAME =
'C:\SQLServer\Data\NaturalKeyTest.mdf', SIZE=8GB, FILEGROWTH=1GB)
LOG ON (NAME='NaturalKeyTestLog', FILENAME =
'C:\SQLServer\Logs\NaturalKeyTest.mdf', SIZE=256MB, FILEGROWTH=128MB);
GO
ALTER DATABASE NaturalKeyTest SET RECOVERY SIMPLE;
GO
USE NaturalKeyTest;
GO
CREATE VIEW GetRand
AS
SELECT RAND() AS RandomNumber;
GO
CREATE FUNCTION RandomString
(
@StringLength INT
)
RETURNS NVARCHAR(max)
AS
BEGIN
DECLARE @cnt INT = 0
DECLARE @str NVARCHAR(MAX) = '';
DECLARE @RandomNum FLOAT = 0;
WHILE @cnt < @StringLength
BEGIN
SELECT @RandomNum = RandomNumber
FROM GetRand;
SET @str = @str + CAST(CHAR((@RandomNum * 64.) + 32) AS NVARCHAR(MAX));
SET @cnt = @cnt + 1;
END
RETURN @str;
END;
GO
CREATE TABLE NaturalTable1
(
NaturalTable1Key NVARCHAR(255) NOT NULL
CONSTRAINT PK_NaturalTable1 PRIMARY KEY CLUSTERED
, Table1TestData NVARCHAR(255) NOT NULL
);
CREATE TABLE NaturalTable2
(
NaturalTable2Key NVARCHAR(255) NOT NULL
CONSTRAINT PK_NaturalTable2 PRIMARY KEY CLUSTERED
, NaturalTable1Key NVARCHAR(255) NOT NULL
CONSTRAINT FK_NaturalTable2_NaturalTable1Key
FOREIGN KEY REFERENCES dbo.NaturalTable1 (NaturalTable1Key)
ON DELETE CASCADE ON UPDATE CASCADE
, Table2TestData NVARCHAR(255) NOT NULL
);
GO
/* insert 1,000,000 rows into NaturalTable1 */
INSERT INTO NaturalTable1 (NaturalTable1Key, Table1TestData)
VALUES (dbo.RandomString(25), dbo.RandomString(100));
GO 1000000
/* insert 10,000,000 rows into NaturalTable2 */
INSERT INTO NaturalTable2 (NaturalTable2Key, NaturalTable1Key, Table2TestData)
SELECT dbo.RandomString(25), T1.NaturalTable1Key, dbo.RandomString(100)
FROM NaturalTable1 T1
GO 10
CREATE TABLE IDTable1
(
IDTable1Key INT NOT NULL CONSTRAINT PK_IDTable1
PRIMARY KEY CLUSTERED IDENTITY(1,1)
, Table1TestData NVARCHAR(255) NOT NULL
CONSTRAINT DF_IDTable1_TestData DEFAULT dbo.RandomString(100)
);
CREATE TABLE IDTable2
(
IDTable2Key INT NOT NULL CONSTRAINT PK_IDTable2
PRIMARY KEY CLUSTERED IDENTITY(1,1)
, IDTable1Key INT NOT NULL
CONSTRAINT FK_IDTable2_IDTable1Key FOREIGN KEY
REFERENCES dbo.IDTable1 (IDTable1Key)
ON DELETE CASCADE ON UPDATE CASCADE
, Table2TestData NVARCHAR(255) NOT NULL
CONSTRAINT DF_IDTable2_TestData DEFAULT dbo.RandomString(100)
);
GO
INSERT INTO IDTable1 DEFAULT VALUES;
GO 1000000
INSERT INTO IDTable2 (IDTable1Key)
SELECT T1.IDTable1Key
FROM IDTable1 T1
GO 10
上記のコードは、データベースと4つのテーブルを作成し、テーブルにデータを入力してテストの準備をします。私が実行したテストコードは次のとおりです。
USE NaturalKeyTest;
GO
DECLARE @loops INT = 0;
DECLARE @MaxLoops INT = 10;
DECLARE @Results TABLE (
FinishedAt DATETIME DEFAULT (GETDATE())
, KeyType NVARCHAR(255)
, ElapsedTime FLOAT
);
WHILE @loops < @MaxLoops
BEGIN
DBCC FREEPROCCACHE;
DBCC FREESESSIONCACHE;
DBCC FREESYSTEMCACHE ('ALL');
DBCC DROPCLEANBUFFERS;
WAITFOR DELAY '00:00:05';
DECLARE @start DATETIME = GETDATE();
DECLARE @end DATETIME;
DECLARE @count INT;
SELECT @count = COUNT(*)
FROM dbo.NaturalTable1 T1
INNER JOIN dbo.NaturalTable2 T2 ON T1.NaturalTable1Key = T2.NaturalTable1Key;
SET @end = GETDATE();
INSERT INTO @Results (KeyType, ElapsedTime)
SELECT 'Natural PK' AS KeyType, CAST((@end - @start) AS FLOAT) AS ElapsedTime;
DBCC FREEPROCCACHE;
DBCC FREESESSIONCACHE;
DBCC FREESYSTEMCACHE ('ALL');
DBCC DROPCLEANBUFFERS;
WAITFOR DELAY '00:00:05';
SET @start = GETDATE();
SELECT @count = COUNT(*)
FROM dbo.IDTable1 T1
INNER JOIN dbo.IDTable2 T2 ON T1.IDTable1Key = T2.IDTable1Key;
SET @end = GETDATE();
INSERT INTO @Results (KeyType, ElapsedTime)
SELECT 'IDENTITY() PK' AS KeyType, CAST((@end - @start) AS FLOAT) AS ElapsedTime;
SET @loops = @loops + 1;
END
SELECT KeyType, FORMAT(CAST(AVG(ElapsedTime) AS DATETIME), 'HH:mm:ss.fff') AS AvgTime
FROM @Results
GROUP BY KeyType;
結果は次のとおりです。
ここで何か間違ったことをしていますか、またはINTキーは25文字のナチュラルキーの3倍高速ですか?