SQL Server - 在int和string之间进行简单的选择和转换

时间:2011-07-10 21:10:39

标签: sql sql-server tsql

我有一个简单的选择语句:

SELECT [dok__Dokument].[dok_Id], 
       [dok__Dokument].[dok_WartUsNetto], 
       [dok__Dokument].[dok_WartUsBrutto], 
       [dok__Dokument].[dok_WartTwNetto], 
       [dok__Dokument].[dok_WartTwBrutto], 
       [dok__Dokument].[dok_WartNetto], 
       [dok__Dokument].[dok_WartVat], 
       [dok__Dokument].[dok_WartBrutto], 
       [dok__Dokument].[dok_KwWartosc] 
  FROM [dok__Dokument] 
 WHERE [dok_NrPelnyOryg] = 2753 
   AND [dok_PlatnikId] = 174 
   AND [dok_OdbiorcaId] = 174 
   AND [dok_PlatnikAdreshId] = 625 
   AND [dok_OdbiorcaAdreshId] = 624

dok_NrPelnyOryg的类型为varchar(30),而不是null。

该表在此列中包含整数和字符串值,并且此select语句被触发了数百万次。

但是最近这个消息开始崩溃:

  

将varchar值'garbi czerwiec B'转换为数据类型int时转换失败。

小解释:该表包含多个“文档”记录,提到的列包含文档原始编号(来自多个不同来源)。

我知道我可以通过在数字周围加上''来解决这个问题,但我更倾向于寻找一个解释,为什么这个曾经起作用,而且现在它没有改变任何东西崩溃。

3 个答案:

答案 0 :(得分:2)

计划更改(由于更改的统计信息,重新编译等)可能导致此数据被更早地评估(例如,完全扫描),或者此特定数据之前未在表中(可能在此开始之前,那里没有糟糕的数据)。如果它应该是一个数字,那么将它设为数字列。如果它还需要允许字符串,那么就像数字一样停止处理它。如果正确地对语句进行参数化并始终传递varchar,则不必担心该值是否用单引号括起来。

答案 1 :(得分:1)

所有这些相等比较操作都遵循SQL Server的Data Type Precedence规则:

  

当操作员组合两个时   不同数据类型的表达式,   数据类型优先级的规则   用数据类型指定   较低的优先级转换为   优先级较高的数据类型。

由于字符类型的优先级低于int类型,因此查询基本上与:

相同
SELECT ...
  FROM [dok__Dokument] 
 WHERE cast([dok_NrPelnyOryg] as int) = 2753
  ...

这有两个影响:

  • 它使WHERE子句中涉及的列上的所有索引都无用
  • 可能会导致转换错误。

你不是第一个遇到这个问题的人,事实上我遇到的几个CSS案件让我最终写了一篇关于这个问题的文章:On SQL Server boolean operator short-circuit

问题的正确解决方案是如果字段值是数字,则列类型应为数字。既然您说数据来自3 rd 方应用程序,您无法更改,最好的解决方案是放弃此应用程序的供应商并选择一个知道正在做什么的应用程序。除此之外,您需要在字符列上搜索字符类型:

SELECT ...
  FROM [dok__Dokument] 
 WHERE [dok_NrPelnyOryg] = '2753'
  ...

在.Net托管的ADO.Net说法中,这意味着您使用如下的SqlCommand:

SqlCommand cmd = new SqlCommand (@"    SELECT ...
      FROM [dok__Dokument] 
     WHERE [dok_NrPelnyOryg] = @nrPelnyOryg
      ... ");
cmd.Parameters.Add("@nrPelnyOryg", SqlDbType.Varchar).Value = "2754";
...

请确保您不会陷入传入NVARCHAR参数(Unicode)以便与VARCHAR列进行比较的简单陷阱,因为之前引用的相同数据类型优先级规则将强制在NVARCHAR类型上进行比较因此再次呈现索引是无用的。陷阱的最简单方法是使用dredded AddWithValue并传入一个字符串值。

答案 2 :(得分:0)

您的查询已停止工作,因为有人将文本字符串插入您使用INT查询的字段中。直到那时,才有可能隐式转换数据,但现在情况已不再如此。

我会检查你的数据,更重要的是,检查模型;正如亚伦所说,你需要在那个领域允许弦乐吗?如果没有,请更改数据类型以防止将来发生这种情况。