我想创建一个带有NOT NULL
bool 列的表。
我使用TINYINT
withCHECK
约束BETWEEN 0 and 1
。约束是新的,因此是可信的
现在我希望 SQL 优化器现在知道该列只能是 0 和 1,所以当我编写查询时,col >= 2
我会在实际执行计划中看到 Constant Scan(就像我检查NULL
或SELECT TOP (0)
但事实并非如此,它选择了表扫描。我是否还需要在此列上有索引?
在下面的测试中,我使用TINYINT
约束CHECK
。基于TINYINT
with boundRULE
和 good old的用户定义类型BIT
。
GO
CREATE TYPE dbo.myBool
FROM [INT] NOT NULL
GO
CREATE RULE dbo.R_Bool AS @value BETWEEN 0 AND 1
go
EXEC sys.sp_bindrule @rulename = N'R_Bool'
, @objname = N'myBool'
GO
DROP TABLE IF EXISTS dbo.RuleTest
CREATE TABLE dbo.RuleTest
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
, oldSchoolBool TINYINT NOT NULL CHECK (oldSchoolBool BETWEEN 0 AND 1)
, customBool dbo.myBool NOT NULL
, myBit BIT NOT NULL
)
;WITH tally (n)
AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS a(n)
CROSS JOIN (VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS b(n)
CROSS JOIN (VALUES (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) AS c(n)
)
INSERT INTO dbo.RuleTest
(oldSchoolBool, customBool, myBit)
SELECT
ABS(CHECKSUM(NewId())) % 2
,ABS(CHECKSUM(NewId())) % 2
,ABS(CHECKSUM(NewId())) % 2
FROM tally t
SET STATISTICS IO ON;
SELECT * FROM dbo.RuleTest rt
WHERE rt.oldSchoolBool IS NULL
SELECT * FROM dbo.RuleTest rt
WHERE rt.oldSchoolBool >=2
go
SELECT * FROM dbo.RuleTest rt
WHERE rt.customBool >=2
go
SELECT * FROM dbo.RuleTest rt
WHERE rt.myBit >= 2
SET STATISTICS IO OFF;
我看到一个用于 NULL 检查的常量扫描和 3 个用于其余部分的表扫描。
查询 2
问题是自动参数化。
在您的情况下,常量
2
被替换为 tinyint 参数@1
而不是文字2
- 因为此参数可能具有值0
,或者1
查询优化器假定检查约束与此相矛盾是无效的。您可以使用以下查询来获取使用矛盾检测的计划(
1=1
防止自动参数化)然后矛盾检测作为简化的一部分发生(请参见此处的优化管道图)。生成的计划被简化为持续扫描
查询 3
规则已被劝阻/弃用约 20 年。2000 BOL 将它们描述为
题目
CREATE RULE
说所以我想查询优化器永远不会信任这些,因为可以执行以下操作并且拥有不符合规则的数据
虽然从技术上讲,维护类似于可信约束的可信规则概念是可能的,但我不相信这存在。
查询 4
CHECK (myBit BETWEEN 0 AND 1)
如果您希望它执行此矛盾检测,则需要添加冗余检查约束或等效项。即使一个不可为空的位只能保存这两个值,如果没有它,您也不会检测到这种矛盾