我有一个 SQL Server 数据库项目:
COMPATIBILITY_LEVEL = 160
SET
选项:
ANSI_NULLS
、ANSI_PADDING
、ANSI_WARNINGS
、ARITHABORT
、CONCAT_NULL_YIELDS_NULL
和 QUOTED_IDENTIFIER
全部 ON
。NUMERIC_ROUNDABORT
是 OFF
ANSI_NULLS
是 ON
。它部署在 Azure SQL 和本地 SQL Server 2022 中。
我创建了一个表,如下所示:
CREATE TABLE dbo.Foobar
(
FooId int NOT NULL IDENTITY PRIMARY KEY,
DateCreated datetime2(7) NULL,
DateDiscombobulated datetime2(7) NULL,
DatePalm datetime2(7) NULL,
DateIsActuallyAFig datetime2(7) NULL
);
还有一个计算列:
ALTER TABLE dbo.Foobar
ADD LeastDate AS LEAST(DateCreated, DateDiscombobulated,
DatePalm, DateIsActuallyAFig),
GreatestDate AS GREATEST(DateCreated, DateDiscombobulated,
DatePalm, DateIsActuallyAFig);
我想在
LeastDate
和 GreatestDate
上创建非聚集索引 - 理想情况下不要将它们设为 PERSISTED
列; 根据文档,必须满足以下要求:
计算列中的所有函数引用必须与表具有相同的所有者。 - ✔️ 没有引用 UDF。
计算列表达式必须是确定性的 - ✔️
LEAST
表达式根据 IsDeterministic = 1
具有 COLUMNPROPERTY()
。
当数据库兼容性级别设置为 90 时,无法在包含 [非 Unicode] 表达式的计算列上创建索引。 - ✔️ 这不是
char
/varchar
表达式。
计算列表达式必须精确:
* _It isn't an expression of the `float` or `real` data types._ - ✔️ Indeed.
* _It doesn't use a `float` or `real` data type in its definition._ - ✔️ Indeed, it does not.
* _The `IsPrecise` property of the `COLUMNPROPERTY` function reports whether a computed_column_expression is precise._ - ❌ `IsPrecise = 0` here.
text
、ntext
、image
- 或从 xml
或 max
长度类型派生。 ✔️SET
选项必须是:ANSI_NULLS
、ANSI_PADDING
、ANSI_WARNINGS
、ARITHABORT
、CONCAT_NULL_YIELDS_NULL
和QUOTED_IDENTIFIER
全部为ON
- 并且NUMERIC_ROUNDABORT
为OFF
- ✔️ 这是
COLUMNPROPERTY
查询:
DECLARE @tableId int = OBJECT_ID('dbo.Foobar');
SELECT
OBJECTPROPERTY( @tableId, 'IsAnsiNullsOn' ) AS IsAnsiNullsOn;
SELECT
c.COLUMN_NAME,
COLUMNPROPERTY( @tableId, c.COLUMN_NAME, 'IsDeterministic') AS IsDeterministic,
COLUMNPROPERTY( @tableId, c.COLUMN_NAME, 'IsIndexable' ) AS IsIndexable,
COLUMNPROPERTY( @tableId, c.COLUMN_NAME, 'IsPrecise' ) AS IsPrecise
FROM
INFORMATION_SCHEMA.COLUMNS AS c
WHERE
c.TABLE_NAME = N'Foobar';
结果:
COLUMN_NAME | 是确定性的 | 可索引 | 很精确 |
---|---|---|---|
FooId | 空 | 1 | 空 |
创建日期 | 空 | 1 | 空 |
日期混乱 | 空 | 1 | 空 |
枣椰树 | 空 | 1 | 空 |
日期实际上是图 | 空 | 1 | 空 |
最少日期 | 1 | 0 | 0 |
最伟大的约会 | 1 | 0 | 0 |
看起来是因为
IsPrecise = 0
,但是对于IsPrecise
列,NULL
应该是datetime2(7)
- 文档说它只是NOT NULL
/float
类型列的
real
(我认为? ).
将计算列转换为
PERSISTED
可以对它们进行索引,但这对于我的情况来说并不理想:
ALTER TABLE dbo.Foobar DROP LeastDate, GreatestDate;
GO
ALTER TABLE dbo.Foobar
ADD LeastDate AS LEAST(DateCreated, DateDiscombobulated,
DatePalm, DateIsActuallyAFig) PERSISTED,
GreatestDate AS GREATEST(DateCreated, DateDiscombobulated,
DatePalm, ateIsActuallyAFig) PERSISTED;
重新运行
COLUMNPROPERTY
查询现在返回:
COLUMN_NAME | 是确定性的 | 可索引 | 很精确 |
---|---|---|---|
最少日期 | 1 | 1 | 0 |
最伟大的约会 | 1 | 1 | 0 |
LEAST()
和 GREATEST()
函数在计算列中使用时始终被评估为不精确。 SQL 引擎不会识别所有函数参数都具有相同类型。
有 2 件事你可以尝试。
ALTER TABLE dbo.Foobar ADD
LeastDate AS CONVERT(datetime2(7), LEAST(
CONVERT(datetime2(7), DateCreated),
CONVERT(datetime2(7), DateDiscombobulated),
CONVERT(datetime2(7), DatePalm),
CONVERT(datetime2(7), DateIsActuallyAFig)
)),
GreatestDate AS CONVERT(datetime2(7), GREATEST(
CONVERT(datetime2(7), DateCreated),
CONVERT(datetime2(7), DateDiscombobulated),
CONVERT(datetime2(7), DatePalm),
CONVERT(datetime2(7), DateIsActuallyAFig)
));
您需要使用
CASE
采取不同的方法。从计算开销的角度来看,我预计这与使用函数不会有太大(如果有的话)不同。
ALTER TABLE dbo.Foobar ADD
LeastDate AS CASE
WHEN DateCreated <= DateDiscombobulated AND DateCreated <= DatePalm AND DateCreated <= DateIsActuallyAFig THEN DateCreated
WHEN DateDiscombobulated <= DatePalm AND DateDiscombobulated <= DateIsActuallyAFig THEN DateDiscombobulated
WHEN DatePalm <= DateIsActuallyAFig THEN DatePalm
ELSE DateIsActuallyAFig
END,
GreatestDate AS CASE
WHEN DateCreated >= DateDiscombobulated AND DateCreated >= DatePalm AND DateCreated >= DateIsActuallyAFig THEN DateCreated
WHEN DateDiscombobulated >= DatePalm AND DateDiscombobulated >= DateIsActuallyAFig THEN DateDiscombobulated
WHEN DatePalm >= DateIsActuallyAFig THEN DatePalm
ELSE DateIsActuallyAFig
END;
注意:否定使用 PERSISTED 显然会对性能产生影响,因为 SQL 需要维护索引,并且对计算日期的任何更新都将导致计算列的重新评估以及可能的索引更改。