繁体   English   中英

SQL 服务器varchar转换

[英]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.19varchar不能,因此出现错误。

如果您不将值用单引号括起来,这将起作用,并且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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM