当日期用作条件时,Unpivot in view 会导致转换为日期时出错

Unpivot in view causes error with conversion into date when date used as criteria

我有一个 table,每个月有一行,金额存储在不同的列中(第 1 天、第 2 天...第 31 天)。我创建了一个视图,该视图每天使用 unpivot 将其拆分为一行,以便我可以针对给定的日期范围进行计算。

当我尝试仅通过 selecting 某些日期范围来使用视图时,只要我在 table 中有 DAY29..DAY31,它就会出错。如果 table 最多包含 28 天,那么它工作正常。

不幸的是,更改 table 结构并不是一个真正的选择,我尝试使用内联函数进行同样的操作,但最终出现相同的错误

Msg 242, Level 16, State 3, Line 52 The conversion of a nvarchar data type to a datetime data type resulted in an out-of-range value.

这是我的 table:

CREATE TABLE CONSUMPTION (
    ID    int NOT NULL,
    YEAR  int NOT NULL,
    MONTH int NOT NULL,
    DAY1  int NULL,
    DAY2  int NULL,
    DAY3  int NULL,
    DAY31 int NULL,
    CONSTRAINT TEST_PK PRIMARY KEY CLUSTERED (ID, YEAR, MONTH)
)

insert into CONSUMPTION values (1,2015,1,10,20,30,310)
insert into CONSUMPTION values (1,2015,2,10,20,30,NULL)

这是我的观点:

create view CONSUMPTION_CALENDAR as
select
    ID,
    YEAR,
    MONTH,
    convert(datetime, substring(COLNAME, 4,2) + '.' + convert(varchar, [MONTH]) + '.' + convert(varchar, [YEAR]), 104) as CONSDATE,
    CONSKG
from
(
    select * from CONSUMPTION
) S
unpivot (CONSKG for COLNAME in (DAY1,DAY2,DAY3,DAY31)) as UP
go

如果我运行像这样,它工作正常:

select * from CONSUMPTION_CALENDAR

但是如果我添加条件,它returns数据,也是失败的:

select * from CONSUMPTION_CALENDAR where CONSDATE >= '20150101'

是否有任何解决方法可以 select 特定日期范围?

编辑:视图中的数据:

ID          YEAR        MONTH       CONSDATE      CONSKG
1           2015        1           2015-01-01    10
1           2015        1           2015-01-02    20
1           2015        1           2015-01-03    30
1           2015        1           2015-01-31   310
1           2015        2           2015-02-01    10
1           2015        2           2015-02-02    20
1           2015        2           2015-02-03    30

SQL Fiddle 中的示例。

选项 1

创建日历 table,日期格式为 suitable,例如D.M.YYYY 可以与逆轴连接。这样就没有从逆透视字符串到日期的转换,所以它不会失败。

create view CONSUMPTION_CALENDAR as
    select
        P.ID,
        C.DAY,
        P.MONTH,
        P.YEAR,
        C.CALENDARDATE as CONSDATE,
        P.CONSKG
    from (
        select
            ID,
            YEAR,
            MONTH,
            ltrim(substring(COLNAME, 4,2)) + '.' + convert(varchar(2), [MONTH]) + '.' + convert(varchar(4), [YEAR]) as STOCKDATESTR,
            CONSKG
        from
        (
            select * from CONSUMPTION
        ) S
        unpivot
        (CONSKG for COLNAME in (DAY1,DAY2,DAY3,DAY4,DAY5,DAY6,DAY7,DAY8,DAY9,DAY10,DAY11,DAY12,DAY13,DAY14,DAY15,DAY16,DAY17,DAY18,DAY19,DAY20,DAY21,DAY22,DAY23,DAY24,DAY25,DAY26,DAY27,DAY28,DAY29,DAY30,DAY31)) as UP
    ) P
    join CALENDAR C on C.DATESTR = P.STOCKDATESTR

CALENDAR table 的日期格式为 D.M.YYYY,DATESTR 中没有前导零,CALENDARDATE 为 Date

选项 2

当 NULLS 更改为 1.1.1900 时,对于这样的视图,提取似乎也可以正常工作:

create view CONSUMPTION_CALENDAR as
select
    ID,
    YEAR,
    MONTH,
    convert(datetime,
      case when CONSKG is NULL then '1.1.1900' else
      substring(COLNAME, 4,2) + '.' + convert(varchar, [MONTH]) + '.' + convert(varchar, [YEAR]) end
    , 104) as CONSDATE,
    CONSKG
from
(
    select * from CONSUMPTION
) S
unpivot (CONSKG for COLNAME in (DAY1,DAY2,DAY3,DAY4,DAY5,DAY6,DAY7,DAY8,DAY9,DAY10,DAY11,DAY12,DAY13,DAY14,DAY15,DAY16,DAY17,DAY18,DAY19,DAY20,DAY21,DAY22,DAY23,DAY24,DAY25,DAY26,DAY27,DAY28,DAY29,DAY30,DAY31)) as UP;

假设 table 中没有错误数据,这应该不会失败。

选项 3

我找到了一种使用 top 来防止该问题的方法。我假设 SQL 服务器无法将 where 谓词从顶部外部移动到其中,因为理论上它可能会更改结果,即使没有订单:

select * from (
  select top 1000000000 * from CONSUMPTION_CALENDAR
) X
where CONSDATE >= convert(datetime, '20150101')

这似乎工作正常,但不能确定在某些情况下是否开始失败。