簡體   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