Sql server 当日期用作条件时,视图中的Unpivot会导致转换为日期的错误
我有一个表,每个月有一行,金额存储在不同的列中(第1天、第2天……第31天)。我创建了一个视图,该视图使用unpivot每天将其拆分为一行,这样我就可以对给定的日期范围进行计算 当我试图通过只选择某个日期范围来使用视图时,只要表中有DAY29..DAY31,它就会出错。如果该表最多只包含28天,那么它可以正常工作 不幸的是,更改表结构并不是一个真正的选项,我用内联函数尝试了同样的方法,但最终还是出现了同样的错误 Msg 242,16级,状态3,第52行nvarchar数据的转换 类型转换为日期时间数据类型导致值超出范围 这是我的桌子:Sql server 当日期用作条件时,视图中的Unpivot会导致转换为日期的错误,sql-server,sql-server-2008,unpivot,Sql Server,Sql Server 2008,Unpivot,我有一个表,每个月有一行,金额存储在不同的列中(第1天、第2天……第31天)。我创建了一个视图,该视图使用unpivot每天将其拆分为一行,这样我就可以对给定的日期范围进行计算 当我试图通过只选择某个日期范围来使用视图时,只要表中有DAY29..DAY31,它就会出错。如果该表最多只包含28天,那么它可以正常工作 不幸的是,更改表结构并不是一个真正的选项,我用内联函数尝试了同样的方法,但最终还是出现了同样的错误 Msg 242,16级,状态3,第52行nvarchar数据的转换 类型转换为日期时
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'
是否有任何变通办法,我可以选择某个日期范围
编辑:视图中的数据:
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
选项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
日历表的日期格式为D.M.YYYY
,在DATESTR中没有前导零,日历日期为Date
选项2
当空值更改为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;
假设表中没有坏数据,这应该不会失败
选项3
通过使用top
,我找到了一种预防问题的方法。我假设SQL Server无法将where谓词从顶部移到顶部,因为理论上它可能会更改结果,即使没有以下顺序:
select * from (
select top 1000000000 * from CONSUMPTION_CALENDAR
) X
where CONSDATE >= convert(datetime, '20150101')
这似乎可以,但不能确定在某些情况下是否会失败。改变结构是不可能的吗?这个数据的“自然”表示形式是未填充的表单,为什么不这样存储?你能在视图(消费日历)中发布一个数据示例吗?这个表在很多地方使用,当然不止一个,所以我真的不想碰它。还为@haytemIt添加了日历格式的数据,它在Fiddle中运行良好,您可以尝试以下两个查询:select*from CONSDATE>=convert(datetime,'20150101');从消费日历中选择*,其中convert(datetime,CONSDATE)>='20150101'@haytem这两个选项在我的小提琴中都失败了。它将获取数据,但它也失败了:我会非常小心依赖它。SQLServer可以移动谓词,甚至可以移动到超出SQL标准允许的范围。一些测试可能表明,这目前“有效”,但几乎任何事情都可能导致服务器在未来生成一个不同的计划,而错误会再次出现(例如,可用内存、表中数据、统计数据、索引的更改都可能导致计划的更改)@Damien_不相信者我也担心这一点。现在我找到了另一种方法,将所有空值更改为固定日期,这听起来应该是可行的,因为无效日期不能有数字。