视图中的取消透视会导致错误,并在将日期用作条件时转换为日期

时间:2016-01-26 09:28:22

标签: sql-server sql-server-2008 unpivot

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

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

不幸的是,更改表结构并不是一个真正的选项,我尝试使用内联函数做同样的事情,但它最终会出现同样的错误

  

Msg 242,Level 16,State 3,Line 52 nvarchar数据的转换   键入日期时间数据类型会导致超出范围的值。

这是我的表:

StatusBar

这是我的观点:

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

是否有任何解决方法可以选择某个日期范围?

编辑:视图中的数据:

select * from CONSUMPTION_CALENDAR where CONSDATE >= '20150101'

SQL Fiddle中的示例。

1 个答案:

答案 0 :(得分:1)

选项1

创建一个日期表,其日期格式合适,例如:可以与unpivot一起加入的D.M.YYYY。这样就没有从unpivot字符串到日期的转换,所以它不会失败。

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表的日期格式为D.M.YYYY,DATESTR中没有前导零,CALENDARDATE为Date

选项2

当NULLS更改为1.1.1900时,fetch似乎也能正常工作:

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;

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

选项3

我能够找到一种方法来使用top来预防问题。我假设SQL Server无法将where谓词从顶部移到顶部,因为理论上它可以改变结果,即使没有顺序:

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

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