为什么 LEAST 列表达式被认为是不精确且不可索引的?

问题描述 投票:0回答:1

我有一个 SQL Server 数据库项目:

  • COMPATIBILITY_LEVEL = 160
  • 具有符合 ANSI 的
    SET
    选项
    • ANSI_NULLS
      ANSI_PADDING
      ANSI_WARNINGS
      ARITHABORT
      CONCAT_NULL_YIELDS_NULL
      QUOTED_IDENTIFIER
      全部
      ON
    • NUMERIC_ROUNDABORT
      OFF
    • 我还验证了我的 SSMS 连接特定
      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
sql-server azure indexing azure-sql-database persisted-column
1个回答
0
投票

LEAST()
GREATEST()
函数在计算列中使用时始终被评估为不精确。 SQL 引擎不会识别所有函数参数都具有相同类型。

有 2 件事你可以尝试。

使用 CONVERT 显式指定数据类型

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比较

您需要使用

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 需要维护索引,并且对计算日期的任何更新都将导致计算列的重新评估以及可能的索引更改。

© www.soinside.com 2019 - 2024. All rights reserved.