SQL 服务器在 COLLATION 更改后停止使用 INDEX
SQL Server stopped using INDEX after COLLATION change
更新:问题已重写,因此一些评论可能不再相关。
在 table 中更改排序规则后,我们在使用索引时遇到了问题。出现了很多扫描件。默认的数据库排序规则是 SQL_Latin1_General_CP1_CI_AS。现在让我们看一下示例:
IF OBJECT_ID('colltest') > 0 DROP TABLE CollTest;
CREATE TABLE dbo.CollTest
(
cs CHAR(8) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL
, ci CHAR(8) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
, cs_latin CHAR(8) COLLATE Latin1_General_CS_AS NOT NULL
, ci_latin CHAR(8) COLLATE Latin1_General_CI_AS NOT NULL
);
CREATE INDEX ix_cs ON dbo.CollTest (cs);
CREATE INDEX ix_ci ON dbo.CollTest (ci);
CREATE INDEX ix_cs_latin ON dbo.CollTest (cs_latin);
CREATE INDEX ix_ci_latin ON dbo.CollTest (ci_latin);
WITH q (n) AS (SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1)
, q100 (n) AS (SELECT 1 FROM q a, q b)
, q10000 (n) AS (SELECT 1 FROM q100 a, q100 b)
, q100000 (n) AS (SELECT 1 FROM q a, q10000 b)
INSERT INTO dbo.CollTest
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) step
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) step
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) step
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) step
FROM q100000;
以下是对以下查询的解释计划:
SELECT cs FROM dbo.colltest WHERE cs = CAST('1000000' AS NVARCHAR(MAX))
SELECT ci FROM dbo.colltest WHERE ci = CAST('1000000' AS NVARCHAR(MAX))
SELECT cs_latin FROM dbo.colltest WHERE cs_latin = CAST('1000000' AS NVARCHAR(MAX))
SELECT ci_latin FROM dbo.colltest WHERE ci_latin = CAST('1000000' AS NVARCHAR(MAX))
所以它表明,当 COLLATION 是 SQL_* 时,它使用扫描,当 Latin* 时,它使用搜索。这是为什么?
这在 Comparing SQL collations to Windows collations
中有解释
For a Windows collation, a comparison of non-Unicode data is
implemented by using the same algorithm as Unicode data. ...
In a SQL collation, SQL Server defines different comparison semantics
for non-Unicode data.
NVARCHAR
的数据类型优先级高于 VARCHAR
,因此当您将 VARCHAR
列与 NVARCHAR
列进行比较时,需要隐式转换该列。
示例数据
DECLARE @T TABLE
(
SQL_CollationVC VARCHAR(1) COLLATE SQL_Latin1_General_CP1_CI_AS,
Win_CollationVC VARCHAR(1) COLLATE Latin1_General_CS_AS,
INDEX SQL_CollationVC(SQL_CollationVC),
INDEX Win_CollationVC(Win_CollationVC)
);
INSERT INTO @T
VALUES
(N'¹',N'¹'),
(N'½',N'½'),
(N'¾',N'¾'),
(N'0',N'0'),
(N'1',N'1');
查询
SELECT Win_CollationVC
FROM @T
WHERE Win_CollationVC = N'1'
能够执行 dynamic seek 并调用 GetRangeThroughConvert
将谓词转换为 varchar
索引上的可搜索索引范围。
查询
SELECT SQL_CollationVC
FROM @T
WHERE SQL_CollationVC = N'1'
你看到一个扫描
注意这returns两个结果¹
和1
还要注意这个
的结果
SELECT SQL_CollationVC
FROM @T
ORDER BY SQL_CollationVC
该列的索引将按上面返回的顺序存储。
+-----------------+
| SQL_CollationVC |
+-----------------+
| ¹ |
| ½ |
| ¾ |
| 0 |
| 1 |
+-----------------+
1
和 ¹
没有在索引中彼此相邻存储,因此不可能将谓词 SQL_CollationVC = N'1'
转换为对该索引的简单查找。
更新:问题已重写,因此一些评论可能不再相关。
在 table 中更改排序规则后,我们在使用索引时遇到了问题。出现了很多扫描件。默认的数据库排序规则是 SQL_Latin1_General_CP1_CI_AS。现在让我们看一下示例:
IF OBJECT_ID('colltest') > 0 DROP TABLE CollTest;
CREATE TABLE dbo.CollTest
(
cs CHAR(8) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL
, ci CHAR(8) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL
, cs_latin CHAR(8) COLLATE Latin1_General_CS_AS NOT NULL
, ci_latin CHAR(8) COLLATE Latin1_General_CI_AS NOT NULL
);
CREATE INDEX ix_cs ON dbo.CollTest (cs);
CREATE INDEX ix_ci ON dbo.CollTest (ci);
CREATE INDEX ix_cs_latin ON dbo.CollTest (cs_latin);
CREATE INDEX ix_ci_latin ON dbo.CollTest (ci_latin);
WITH q (n) AS (SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1)
, q100 (n) AS (SELECT 1 FROM q a, q b)
, q10000 (n) AS (SELECT 1 FROM q100 a, q100 b)
, q100000 (n) AS (SELECT 1 FROM q a, q10000 b)
INSERT INTO dbo.CollTest
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) step
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) step
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) step
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) step
FROM q100000;
以下是对以下查询的解释计划:
SELECT cs FROM dbo.colltest WHERE cs = CAST('1000000' AS NVARCHAR(MAX))
SELECT ci FROM dbo.colltest WHERE ci = CAST('1000000' AS NVARCHAR(MAX))
SELECT cs_latin FROM dbo.colltest WHERE cs_latin = CAST('1000000' AS NVARCHAR(MAX))
SELECT ci_latin FROM dbo.colltest WHERE ci_latin = CAST('1000000' AS NVARCHAR(MAX))
所以它表明,当 COLLATION 是 SQL_* 时,它使用扫描,当 Latin* 时,它使用搜索。这是为什么?
这在 Comparing SQL collations to Windows collations
中有解释For a Windows collation, a comparison of non-Unicode data is implemented by using the same algorithm as Unicode data. ... In a SQL collation, SQL Server defines different comparison semantics for non-Unicode data.
NVARCHAR
的数据类型优先级高于 VARCHAR
,因此当您将 VARCHAR
列与 NVARCHAR
列进行比较时,需要隐式转换该列。
示例数据
DECLARE @T TABLE
(
SQL_CollationVC VARCHAR(1) COLLATE SQL_Latin1_General_CP1_CI_AS,
Win_CollationVC VARCHAR(1) COLLATE Latin1_General_CS_AS,
INDEX SQL_CollationVC(SQL_CollationVC),
INDEX Win_CollationVC(Win_CollationVC)
);
INSERT INTO @T
VALUES
(N'¹',N'¹'),
(N'½',N'½'),
(N'¾',N'¾'),
(N'0',N'0'),
(N'1',N'1');
查询
SELECT Win_CollationVC
FROM @T
WHERE Win_CollationVC = N'1'
能够执行 dynamic seek 并调用 GetRangeThroughConvert
将谓词转换为 varchar
索引上的可搜索索引范围。
查询
SELECT SQL_CollationVC
FROM @T
WHERE SQL_CollationVC = N'1'
你看到一个扫描
注意这returns两个结果¹
和1
还要注意这个
的结果SELECT SQL_CollationVC
FROM @T
ORDER BY SQL_CollationVC
该列的索引将按上面返回的顺序存储。
+-----------------+
| SQL_CollationVC |
+-----------------+
| ¹ |
| ½ |
| ¾ |
| 0 |
| 1 |
+-----------------+
1
和 ¹
没有在索引中彼此相邻存储,因此不可能将谓词 SQL_CollationVC = N'1'
转换为对该索引的简单查找。