简体   繁体   English

SQL 服务器varchar转换

[英]SQL Server varchar conversion

Bumped into an issue.碰到一个问题。

SQL query 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)

getting message:收到消息:

Msg 245, Level 16, State 1, Line 1消息 245,第 16 级,State 1,第 1 行
Conversion failed when converting the varchar value '24.19' to data type int.将 varchar 值“24.19”转换为数据类型 int 时转换失败。

The problem is that columns OldVal and NewVal store all kinds of data/numbers/text and is set to varchar(300).问题是列 OldVal 和 NewVal 存储各种数据/数字/文本并设置为 varchar(300)。

Why is it trying to convert into 'int' if the filed is set as 'varchar'?如果文件设置为'varchar',为什么它试图转换为'int'?

Thank you!谢谢!

The problem is that in your VALUES table construct you have varying data in your rows.问题是在您的VALUES表构造中,您的行中有不同的数据。 if we add some formatting, this becomes quickly apparent:如果我们添加一些格式,这很快就会变得明显:

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);

Notice that the 2 last columns have different data types, the first has 2 varchar values (that are clearly numerical and thus should not be inside single quotes ( ' )), and the latter are both int values.请注意,最后两列具有不同的数据类型,第一列具有 2 个varchar值(显然是数字,因此不应在单引号 ( ' ) 内),而后者都是int值。

As you have different data types, and a column must be made up of the same data types through out, then values of a lower data type precedence are converted to the type with the highest.由于您有不同的数据类型,并且一列必须由相同的数据类型组成,因此较低数据类型优先级的值将转换为具有最高优先级的类型。 varchar has one of the lowest, and so the values '0' and '24.19' are implicitly converted to int s. varchar具有最低值之一,因此值'0''24.19'被隐式转换为int s。 For '0' that's fine, it's an int as well, however, the varchar representing the numerical value 24.19 cannot , hence the error.对于'0'很好,它也是一个int ,但是,表示数值24.19varchar不能,因此出现错误。

If you don't wrap the values in single quotes, this will work, and the 1 will be implicitly converted to a decimal(4,2) .如果您不将值用单引号括起来,这将起作用,并且1将被隐式转换为decimal(4,2)

Of course, the real problem is this:当然,真正的问题是这样的:

The problem is that columns OldVal and NewVal store all kinds of data/numbers/text and is set to varchar(300).问题是列 OldVal 和 NewVal 存储各种数据/数字/文本并设置为 varchar(300)。

That is a significant design flaw;这是一个重大的设计缺陷; fix that.解决这个问题。 A column can only be made up of one data type, and storing data that isn't meant to be in a varchar as a varchar will never work out well.一列只能由一种数据类型组成,并且将不应该在varchar中的数据存储为varchar永远不会很好。 varchar is NOT a one size fits all data type . varchar 不是一种适合所有数据类型的尺寸

I would personally suggest storing the old row as a whole, not just the column that changed, along with the meta data you need.我个人建议将旧行作为一个整体存储,而不仅仅是更改的列,以及您需要的元数据。 Then everything is store as it should be, and the "new" row is available in the original table.然后一切都按原样存储,“新”行在原始表中可用。 Especially as a solution like what you have will fall over the moment you update 2 or more columns at the same time.特别是当您同时更新 2 列或更多列时,您所拥有的解决方案将会崩溃。

This surprised me, and I've been doing SQL Server for years.这让我很惊讶,多年来我一直在做 SQL 服务器。 Larnu has already answered very well but I feel like I can add something. Larnu 已经回答得很好,但我觉得我可以补充一些东西。

The surprise here is that although you are inserting a mixed bag of values into a varchar column, instead of implicitly converting them to varchar, SQL Server is deciding to implicitly convert them to int.这里令人惊讶的是,尽管您将混合的值包插入 varchar 列,而不是将它们隐式转换为 varchar,SQL 服务器决定将它们隐式转换为 int。 This happens because there is an int in the list, and it has higher data type precedence, so SQL Server tries to cast everything to that type.发生这种情况是因为列表中有一个 int,并且它具有更高的数据类型优先级,因此 SQL 服务器尝试将所有内容强制转换为该类型。 If you were just inserting one row at a time, it would be fine.如果您一次只插入一行,那就没问题了。 But because you are inserting two rows, SQL Server implictly invents a table structure for them, without checking what you actually want to do with them.但是因为您要插入两行,SQL Server 隐含地为它们发明了一个表结构,而没有检查您实际上想要对它们做什么。 From where I'm sitting, this does look kindof dumb on SQL Servers part.从我坐的地方看,这在 SQL 服务器部分看起来确实有点愚蠢。 I'm sure it has its reasons.我敢肯定它有它的原因。

I could say how you could design everything differently blah blah but sometimes people don't have that choice, so I'll try and solve your immediate problem.我可以说你可以如何设计不同的东西等等等等,但有时人们没有这样的选择,所以我会尝试解决你的直接问题。

You can force the cast to be made to varchar instead of int by wrapping all the values in an explicit CAST statement, like this:您可以通过将所有值包装在显式 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)));

This may look crazy but if for example your INSERT statements were being generated in code, this is how to generate them and make the casting happen the way you want it to.这可能看起来很疯狂,但是例如,如果您的 INSERT 语句是在代码中生成的,那么这就是如何生成它们并使转换按您希望的方式发生的方法。

Here's a SQL Fiddle of the problem, which throws an error: http://sqlfiddle.com/#!18/dd7fe8/1这是问题的 SQL Fiddle,它会引发错误: http://sqlfiddle.com/#!18/dd7fe8/1

Here's a SQL Fiddle of my solution http://sqlfiddle.com/#!18/dd7fe8/4这是我的解决方案http://sqlfiddle.com/#!18/dd7fe8/4的 SQL 小提琴

Thank you all for your kind comments!谢谢大家的善意评论!

To answer some of the concerns, I did not do parameterized because all data is tightly (.) validated, I wish I could use multiple columns for each data type, or write old/new rows, the constrain is that this is log table that logs historical changes to all the fields on different pages.为了回答一些问题,我没有进行参数化,因为所有数据都经过严格(。)验证,我希望我可以为每种数据类型使用多个列,或者写入旧/新行,限制是这是日志表记录不同页面上所有字段的历史更改。 and users can change them multiple times.并且用户可以多次更改它们。 0 and 1 I had as kinda switch (yes/no) flag when user completes certain action.当用户完成某些操作时,我有 0 和 1 作为有点切换(是/否)标志。 Everything is logged.一切都被记录下来。

And yes, when I put in quotes '0' and '1' and went through.是的,当我加上引号“0”和“1”并通过时。 Thank you Larnu and Codeulike for the answer and the feedback!感谢 Larnu 和 Codeulike 的回答和反馈!

I felt it is weird that SQL is fine with 'xxxx' and 1 on separate queries but not fine when those values are passed in one query.我觉得奇怪的是 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