【问题标题】:Unable to make computed column indexable无法使计算列可索引
【发布时间】:2019-03-09 19:06:05
【问题描述】:
CREATE FUNCTION dbo.FN_GET_YEAR(@ID int) RETURNS int WITH SCHEMABINDING AS
BEGIN
    RETURN (SELECT YEAR(begin_date) FROM dbo.tableName WHERE id = @ID);
END

GO

CREATE TABLE test_table (
    id int,
    fk_id int,
    test AS dbo.FN_GET_YEAR(id)
);

SELECT COLUMNPROPERTY(OBJECT_ID('test_table'), 'test', 'IsIndexable') AS IsIndexableColumn;

以上是我的实际问题的非常简化的摘录。我有一个函数可以返回给定日期的年份,然后该函数用作计算列。我希望计算列是可索引的。

但是,尽管尝试了所有在线建议,但我不确定我缺少什么。我使用了“WITH SCHEMABINDING”关键字,还尝试了使用和不使用 CONVERT/CAST。问题是该列不是确定性的,但文档还说 YEAR() 是确定性的。

如果我只是在函数中返回一个静态声明的值,那么该列将变为可索引的。 YEAR() 似乎打破了它。

编辑:

我不想发布我的原始查询以保持简单,但也许我让它太简单了。我更新了更接近实际的函数查询。

【问题讨论】:

  • GETDATE() 是不确定的。请参阅docs.microsoft.com/en-us/sql/t-sql/functions/…。所以YEAR(GETDATE()) 也是不确定的
  • 当某件事每秒都在变化时,你可以确定它吗?
  • 当表dbo.tableName 更新时,您希望索引中的数据发生什么变化?
  • @DavidBrowne-Microsoft 我需要将外键和年份组合限制为每年一次。所以它将是一个唯一的索引。此外,年份列将用于其他一些查询

标签: sql-server indexing deterministic non-deterministic


【解决方案1】:

“我需要将外键和年份组合限制为一年一次。所以它将是一个唯一索引。”

我不确定您尝试的是一个好主意,但它肯定不能通过计算列上的索引来强制执行,因为需要更改 either 表预防。

你可以用索引视图来做这种事情。例如:

drop view if exists v_test_table_year
drop table if exists test_table
drop table if exists tablename
go
create table tableName(id int, begin_date datetime)

go

CREATE TABLE test_table (
    id int,
    fk_id int--,
   -- test AS dbo.FN_GET_YEAR(id)
);

go
create or alter view v_test_table_year
with schemabinding
as
select year(tn.begin_date) year
from dbo.test_table t
join dbo.tableName tn
  on t.fk_id = tn.id

 go
 create unique clustered index ci_v_test_table_year
 on v_test_table_year(year)

 go

 insert into tableName(id,begin_date) values (1,'2017-01-01')
 insert into tableName(id,begin_date) values (2,'2017-02-01')

 insert into test_table(id,fk_id) values (1,1) --ok
 insert into test_table(id,fk_id) values (2,2) --fails

【讨论】:

    【解决方案2】:

    我有一个函数可以返回给定日期的年份,然后该函数用作计算列

    这不是您发布的内容。您发布了一个返回当前年份的函数,这显然不是确定性的。

    您可以编写一个函数来返回给定日期的年份并在索引计算列中使用它:

    CREATE FUNCTION dbo.FN_GET_YEAR(@d datetime) RETURNS int WITH SCHEMABINDING AS
    BEGIN
        RETURN YEAR(@d);
    END
    
    GO
    
    CREATE TABLE test_table (
        id int,
        dateCol datetime,
        test AS dbo.FN_GET_YEAR(dateCol)
    );
    
    SELECT COLUMNPROPERTY(OBJECT_ID('test_table'), 'test', 'IsIndexable') AS IsIndexableColumn;
    
    create index ix_test_table_test on test_table(test)
    

    【讨论】:

      猜你喜欢
      • 2012-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多