[英]SQL Server varchar conversion
碰到一个问题。
SQL查询
INSERT INTO LogItems (Date, Type, UserID, UserName, RefID, Param, OldVal, NewVal)
VALUES (SYSDATETIMEOFFSET(), '17', '31', 'Nick', '1029', 'Discount', '0', '24.19')
, (SYSDATETIMEOFFSET(), '17', '31', 'Nick', '1029', 'CM applied', 0, 1)
收到消息:
消息 245,第 16 级,State 1,第 1 行
将 varchar 值“24.19”转换为数据类型 int 时转换失败。
问题是列 OldVal 和 NewVal 存储各种数据/数字/文本并设置为 varchar(300)。
如果文件设置为'varchar',为什么它试图转换为'int'?
谢谢!
问题是在您的VALUES
表构造中,您的行中有不同的数据。 如果我们添加一些格式,这很快就会变得明显:
INSERT INTO LogItems (Date, Type, UserID, UserName, RefID, Param, OldVal, NewVal)
VALUES (SYSDATETIMEOFFSET(), '17', '31', 'Nick', '1029', 'Discount', '0', '24.19'),
(SYSDATETIMEOFFSET(), '17', '31', 'Nick', '1029', 'CM applied', 0 , 1);
请注意,最后两列具有不同的数据类型,第一列具有 2 个varchar
值(显然是数字,因此不应在单引号 ( '
) 内),而后者都是int
值。
由于您有不同的数据类型,并且一列必须由相同的数据类型组成,因此较低数据类型优先级的值将转换为具有最高优先级的类型。 varchar
具有最低值之一,因此值'0'
和'24.19'
被隐式转换为int
s。 对于'0'
很好,它也是一个int
,但是,表示数值24.19
的varchar
不能,因此出现错误。
如果您不将值用单引号括起来,这将起作用,并且1
将被隐式转换为decimal(4,2)
。
当然,真正的问题是这样的:
问题是列 OldVal 和 NewVal 存储各种数据/数字/文本并设置为 varchar(300)。
这是一个重大的设计缺陷; 解决这个问题。 一列只能由一种数据类型组成,并且将不应该在varchar
中的数据存储为varchar
永远不会很好。 varchar 不是一种适合所有数据类型的尺寸。
我个人建议将旧行作为一个整体存储,而不仅仅是更改的列,以及您需要的元数据。 然后一切都按原样存储,“新”行在原始表中可用。 特别是当您同时更新 2 列或更多列时,您所拥有的解决方案将会崩溃。
这让我很惊讶,多年来我一直在做 SQL 服务器。 Larnu 已经回答得很好,但我觉得我可以补充一些东西。
这里令人惊讶的是,尽管您将混合的值包插入 varchar 列,而不是将它们隐式转换为 varchar,SQL 服务器决定将它们隐式转换为 int。 发生这种情况是因为列表中有一个 int,并且它具有更高的数据类型优先级,因此 SQL 服务器尝试将所有内容强制转换为该类型。 如果您一次只插入一行,那就没问题了。 但是因为您要插入两行,SQL Server 隐含地为它们发明了一个表结构,而没有检查您实际上想要对它们做什么。 从我坐的地方看,这在 SQL 服务器部分看起来确实有点愚蠢。 我敢肯定它有它的原因。
我可以说你可以如何设计不同的东西等等等等,但有时人们没有这样的选择,所以我会尝试解决你的直接问题。
您可以通过将所有值包装在显式 CAST 语句中来强制强制转换为 varchar 而不是 int,如下所示:
INSERT INTO LogItems (Date, Type, UserID, UserName, RefID, Param, OldVal, NewVal)
VALUES (SYSDATETIMEOFFSET(), '17', '31', 'Nick', '1029', 'Discount', Cast('0' as varchar(300)), Cast('24.19' as varchar(300)))
, (SYSDATETIMEOFFSET(), '17', '31', 'Nick', '1029', 'CM applied', Cast(0 as varchar(300)), Cast(1 as varchar(300)));
这可能看起来很疯狂,但是例如,如果您的 INSERT 语句是在代码中生成的,那么这就是如何生成它们并使转换按您希望的方式发生的方法。
这是问题的 SQL Fiddle,它会引发错误: http://sqlfiddle.com/#!18/dd7fe8/1
这是我的解决方案http://sqlfiddle.com/#!18/dd7fe8/4的 SQL 小提琴
谢谢大家的善意评论!
为了回答一些问题,我没有进行参数化,因为所有数据都经过严格(。)验证,我希望我可以为每种数据类型使用多个列,或者写入旧/新行,限制是这是日志表记录不同页面上所有字段的历史更改。 并且用户可以多次更改它们。 当用户完成某些操作时,我有 0 和 1 作为有点切换(是/否)标志。 一切都被记录下来。
是的,当我加上引号“0”和“1”并通过时。 感谢 Larnu 和 Codeulike 的回答和反馈!
我觉得奇怪的是 SQL 在单独的查询中可以使用 'xxxx' 和 1,但在一个查询中传递这些值时就不行了。
INSERT INTO LogItems (Date, Type, UserID, UserName, RefID, Param, OldVal, NewVal) VALUES (SYSDATETIMEOFFSET(), '17', '31', 'Nick', '1029', 'Discount', '0', '24.19'), (SYSDATETIMEOFFSET(), '17', '31', 'Nick', '1029', 'CM applied', '0', '1');
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.