【问题标题】:SQL Execution Error. Error converting data type nvarchar to realSQL 执行错误。将数据类型 nvarchar 转换为 real 时出错
【发布时间】:2013-04-15 21:25:56
【问题描述】:

目前我正在对开始失败的 SQL Server 2008 查询进行故障排除。这是查询:

SELECT     TOP (100) PERCENT dbo.tblBenchmarkData.FieldDataSetID, dbo.tblBC.BCID, CAST(dbo.tblBenchmarkData.DataValue AS float(8)) AS DataValue, 
                  dbo.tblBC.BCMnemonic, dbo.tblDataType.DataTypeMnemonic
FROM         dbo.tblFieldDataSet RIGHT OUTER JOIN
                  dbo.tblBenchmarkData ON dbo.tblFieldDataSet.FieldDataSetID = dbo.tblBenchmarkData.FieldDataSetID LEFT OUTER JOIN
                  dbo.tblBC LEFT OUTER JOIN
                  dbo.tblDataType ON dbo.tblBC.DataTypeID = dbo.tblDataType.DataTypeID RIGHT OUTER JOIN
                  dbo.tblZEGCode ON dbo.tblBC.BCID = dbo.tblZEGCode.BCID ON dbo.tblBenchmarkData.ZEGCodeID = dbo.tblZEGCode.ZEGCodeID
WHERE     (dbo.tblDataType.DataTypeID = '{5951994B-BF47-4117-805D-B8F85FAB76A8}') AND (dbo.tblFieldDataSet.OriginalFieldDataSetID IS NULL) AND 
                  (dbo.tblFieldDataSet.Duplicate = 0)
ORDER BY dbo.tblBC.BCMnemonic, DataValue 

当我删除强制转换并执行查询时,它返回大约 1400000 行,因此我获取 DataValue 结果并针对此输出运行一个小型 C# 程序以验证所有数据实际上是数字:

List<String> lstLinesOfFile = new List<string>();
Int64 intLineCounter = 0;
ReadFile("data.txt");
double dblNum;

foreach (string strValue in lstLinesOfFile)
{
   bool isNum = double.TryParse(strValue, out dblNum);

   if (!isNum)
   {
      Debug.WriteLine("Line: " + Convert.ToString(intLineCounter) + ", Value = " + strValue);
   }
   intLineCounter++;
}

该程序表明没有不是数字的数据行,所以有人对我为什么会收到此错误有任何建议吗? TIA。

更新:
这是我编写的代码,用于验证它是否正在检查每一行数据:

List<String> lstLinesOfFile = new List<string>();
Int64 intLineCounter = 0;
ReadFile("data.txt");
double dblNum;

foreach (string strValue in lstLinesOfFile)
{
   bool isNum = double.TryParse(strValue, out dblNum);

   if (!isNum)
   {
      Debug.WriteLine("Line: " + Convert.ToString(intLineCounter) + ", Value = " + strValue);
   }
   else
   {
       Debug.WriteLine("Line: " + Convert.ToString(intLineCounter) + ", Number = " + strValue);
   }
   intLineCounter++;
}

这会给出如下结果:

....

Line: 241564, Number = 1843.2
Line: 241565, Number = 18430
Line: 241566, Number = 18430.9
Line: 241567, Number = 18431.6
Line: 241568, Number = 18433.9
Line: 241569, Number = 1844.52

....

更新 2: 从完整的原始视图粘贴上面的代码。

【问题讨论】:

  • 一年前你不是在同一张桌子上问过几乎同样的问题吗?您接受的答案是否仍然没有帮助(即根据 ISNUMERIC 搜索行)? stackoverflow.com/questions/9136722/…
  • 当我尝试使用 CASE WHEN 时,我仍然遇到同样的错误。

标签: c# sql


【解决方案1】:

使用它来确定哪些值不能被 SQL 解析

select * from tblBenchData where ISNUMERIC(DataValue) = 0

或者这个

select * from tblBenchData where DataValue like '%[a-Z]%'

我的猜测是文化问题(例如“,”与“.”)

【讨论】:

  • 感谢您的反馈。这个查询返回零行,所以我很困惑。
  • 它仍然返回带有 [a-Z] 的零行。
【解决方案2】:

出现此问题是因为有数量惊人的奇数字符串将通过ISNUMERIC() 测试,但无法将CAST() 转换为浮点数(并且可能仍可被double.TryParse 转换)。我所知道的三个是:

  • '+'
  • '.'
  • '-'

这是捕捉其中一些的一种方法:

SELECT DataValue
FROM   dbo.tblBenchData
WHERE  ISNUMERIC('0'+DataValue) = 0

奇怪的是,这将要么正确转换,要么拒绝其中大多数而不会出错:

SELECT CAST('0'+DataValue As float(8)) As DataValue
FROM   dbo.tblBenchData
WHERE  ISNUMERIC('0'+DataValue) = 0

但仍有一些奇怪的案例可以通过。


好的,我忘记了这个技巧不喜欢有效的负值。

而且,至于“其他”奇怪的情况,我真的不记得它们了,我也从来没有在任何地方看到过它们(真的很烦,我知道)。然而,大多数问题字符串来自于各种不同的数值解析器如何看待所谓的“退化情况”,即单个字符串。

因此,尝试解决这两个问题的一种方法是预先检查长度为 1 的字符串,然后对其进行特殊处理。因此,将您的原始查询更改为类似这样的查询应该可以:

;WITH cteBench As
(
    SELECT  *
        ,   CASE
                WHEN DataValue IN('-','.','+')  THEN '0'
                WHEN Len(DataValue) = 1 AND DataValue BETWEEN '0' And '9'
                                                THEN DataValue
                WHEN Len(DataValue) = 1         THEN ''
                WHEN DataValue LIKE '-%'        THEN DataValue
                ELSE                    '0'+DataValue
            END As FixedDataValue
    FROM    dbo.tblBenchmarkData.DataValue
)
SELECT     TOP (100) PERCENT cteBench.FieldDataSetID, dbo.tblBC.BCID, CAST(cteBench.FixedDataValue AS float(8)) AS DataValue, 
                  dbo.tblBC.BCMnemonic, dbo.tblDataType.DataTypeMnemonic
FROM         dbo.tblFieldDataSet RIGHT OUTER JOIN
                  cteBench ON dbo.tblFieldDataSet.FieldDataSetID = cteBench.FieldDataSetID LEFT OUTER JOIN
                  dbo.tblBC LEFT OUTER JOIN
                  dbo.tblDataType ON dbo.tblBC.DataTypeID = dbo.tblDataType.DataTypeID RIGHT OUTER JOIN
                  dbo.tblZEGCode ON dbo.tblBC.BCID = dbo.tblZEGCode.BCID ON cteBench.ZEGCodeID = dbo.tblZEGCode.ZEGCodeID
WHERE     (dbo.tblDataType.DataTypeID = '{5951994B-BF47-4117-805D-B8F85FAB76A8}')
 AND      (dbo.tblFieldDataSet.OriginalFieldDataSetID IS NULL)
 AND      (dbo.tblFieldDataSet.Duplicate = 0)
 AND      ISNUMERIC(FixedDataValue) = 1
ORDER BY dbo.tblBC.BCMnemonic, DataValue

【讨论】:

  • 是的,这就是问题所在 - 您的查询将返回所有负值!
  • 请提供有关可以通过的少数奇怪案例的更多详细信息。
  • 感谢 cteBench 表表达式。它仍然返回错误。由于我无法解释的原因,使用 TOP 100 执行查询会导致错误,而任何其他数字都可以正常工作。
  • 在您的数据顺序末尾的列中可能还有一个额外的奇怪字符串,但我的过滤器没有捕获到。这些东西真的很让人头疼。
【解决方案3】:

您在该查询中有右外连接和左外连接语法;但是您在右/左连接表上使用了 where 子句。这有点讨厌,可能没有按照您的预期进行。

我首先会在tblBenchData 表中查看返回为空的DataSetID。

类似:

SELECT *
  FROM tblDataSet
  WHERE DataSetID NOT EXISTS IN (SELECT DataSetID FROM tblBenchData)

接下来,我猜您在复制/粘贴时遗漏了一些内容,因为tblBenchCode 上的左外连接没有ON 条件。您能否提供实际使用的查询?

旁注:我不完全确定您为什么拥有TOP(100) PERCENT 部分。我知道在视图定义中允许 order by 是一个不可靠的技巧,但它在今天毫无意义,在您提供的查询中当然毫无意义......除非这是一个古老的 SQL 服务器。


另一种可能性是,正如 Loud 指出的那样,您一年前的问题表明您的值在 scientific notation...

【讨论】:

  • 我查了科学记数法,本例中没有。请查看更新 2 以获取完整视图。
  • 仅供参考,前 100 % 由 SSMS 自动输入。
【解决方案4】:

乍一看,您似乎没有将 c# 代码连接到 sql 查询。

下面的行将 1stLinesOffline 设置为一个空的字符串列表,但您从未将其设置为其他任何内容,因此 for 循环将永远不会运行。

List lstLinesOfFile = new List();

我希望看到

List 1stLinesOfFile = mySqlResults;

其中 mySqlResults 是您的 SQL 的结果数据集。

【讨论】:

  • 感谢您的评论。实际上,我只是从 SSMS 复制并粘贴了结果,并使用我创建的函数 ReadFile("data.txt"); 读取了该数据。当我在 Visual Studio 中运行 c# 项目时,我验证它检查了所有 140 万行数据。详情请查看我的更新。
【解决方案5】:

上面的SELECT TOP (100) PERCENT dbo.tblBenchmarkData.FieldDataSetID, dbo.tblBC.BCID...其实就是一个叫qryFieldParameterBCNumericLookup的视图。当我执行这个命令时:

SELECT TOP 100 * FROM MyDatabase.dbo.qryFieldParameterBCNumericLookup

我得到错误:

Msg 8114, Level 16, State 5, Line 1
Error converting data type nvarchar to real.

当我执行这些命令中的任何一个时,它们都可以正常工作,没有错误:

SELECT * FROM MyDatabase.dbo.qryFieldParameterBCNumericLookup
SELECT TOP 50 * FROM MyDatabase.dbo.qryFieldParameterBCNumericLookup
SELECT TOP 1000 * FROM MyDatabase.dbo.qryFieldParameterBCNumericLookup

【讨论】:

  • 我建议做两件事。首先,TOP 是 2008 年的一个函数。意思是你可以做SELECT TOP(100) * FROM xxx。试试看。其次,将您的表和视图定义发送给 sql server 支持并询问他们发生了什么。
猜你喜欢
  • 2020-06-02
  • 1970-01-01
  • 2017-05-06
  • 2012-06-16
  • 1970-01-01
  • 2016-01-20
  • 2021-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多