【问题标题】:Table adapter query gives an error that is not reproducible in SQL Server Management Studio表适配器查询给出了在 SQL Server Management Studio 中不可重现的错误
【发布时间】:2010-09-17 08:55:21
【问题描述】:

我正在维护一些在 Visual Studio 中的表适配器设计器中定义的查询,这些查询用于 Windows 窗体应用程序 (.NET 2.0) 中的一些报告中。 当我运行应用程序并执行特定查询时,出现错误:将表达式转换为数据类型 smallmoney 的算术溢出错误。我很惊讶,因为查询应该产生相当小的数量,所以我使用 SQL 分析器捕获了查询,并在 SQL Server Management Studio 中运行了完全相同的查询(显然在同一个数据库上)。这里查询运行没有问题,smallmoney 是“33.00”;不在 214,748.3647 边界附近。

为了使调试复杂化,此问题仅发生在客户端的 QA 环境中,并且无法在本地重现(并且出于法律原因,无法将数据库复制到开发环境中)。这使得调试周期非常缓慢,因为在客户端环境中构建和部署新版本最多需要 30 分钟,所以我非常感谢一些提示,这些提示将使我通过尽可能少的实验来查明这个问题。在 SQL Studio 中摆弄查询对我没有多大帮助,因为我无法让它产生同样的错误。

这里是查询:

SELECT        CONVERT(varchar, Events.Occurred, 102) AS Day, Users.Name, COUNT(*) AS Deleted_Invoices, SUM(i.TotalExVat + i.TotalVat) AS Total
FROM            Events WITH (nolock) INNER JOIN
                         Users WITH (nolock) ON Events.UserID = Users.UserID INNER JOIN
                         Types AS t WITH (nolock) ON t.TypeID = Events.TypeID INNER JOIN
                         InvoicesEvents AS ie ON ie.EventID = Events.EventID INNER JOIN
                         Invoices AS i ON i.InvoiceID = ie.InvoiceID
WHERE        (Events.Occurred BETWEEN @startDate AND @endDate) AND (t.Name = 'InvoiceDeleted')
GROUP BY CONVERT(varchar, Events.Occurred, 102), Users.Name
ORDER BY Day, Users.Name

TotalExVat 和 TotalVat 是 smallmoney 不为空。数据表中的“Total”字段映射到“System.Decimal”。我可以尝试在表达式中将 smallmoney 转换为 money,但是当它在 SQL 工作室中运行良好时,为什么我必须这样做呢?

我得到的例外是: 异常类型:System.Data.SqlClient.SqlException 异常消息:将表达式转换为数据类型 smallmoney 时出现算术溢出错误。

   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlDataReader.HasMoreRows()
   at System.Data.SqlClient.SqlDataReader.ReadInternal(Boolean setTimeout)
   at System.Data.SqlClient.SqlDataReader.Read()
   at System.Data.Common.DataAdapter.FillLoadDataRow(SchemaMapping mapping)
   at System.Data.Common.DataAdapter.FillFromReader(DataSet dataset, DataTable datatable, String srcTable, DataReaderContainer dataReader, Int32 startRecord, Int32 maxRecords, DataColumn parentChapterColumn, Object parentChapterValue)
   at System.Data.Common.DataAdapter.Fill(DataTable[] dataTables, IDataReader dataReader, Int32 startRecord, Int32 maxRecords)
   at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.Fill(DataTable[] dataTables, Int32 startRecord, Int32 maxRecords, IDbCommand command, CommandBehavior behavior)
   at System.Data.Common.DbDataAdapter.Fill(DataTable dataTable)
   at ... (calling code)

更新

将 SUM(i.TotalExVat + i.TotalVat) 更改为 SUM(i.TotalExVat) + SUM(i.TotalVat) 消除了错误,但我仍然不明白为什么,因为生成的没有 smallmoney 溢出结果。

更新 2

新问题。现在 smallmoney 铸造问题消失了,但现在我遇到了超时问题。如果在 SSMS 中运行,同一报告中使用的另一个查询将在大约 5-6 秒内运行。如果在表适配器中运行,它会在 10 分钟后超时。其他查询按预期运行,产生与 SSMS 中相同的结果。这支持了我的怀疑,即当我的表适配器尝试查询数据库时出现了一些问题。

更新 3

这开始变得奇怪了。 smallmoney 问题查询是用于生成报告的一系列查询中的第五个填充查询。在我应用第一次更新中提到的修复后,我在第一个查询中遇到超时。当 smallmoney 在后面的查询中溢出时,该查询运行没有问题。 这可能是什么原因?

当 smallmoney 查询给出并出错时查询运行,而在其工作时不运行:

SELECT        u.Name AS Username, rea.Text AS DeleteReason, COUNT(*) AS DeletedRegistrations, SUM(r.Shipments) AS DeletedShipments
FROM            RecordingsEvents AS re WITH (nolock) INNER JOIN
                         Events AS e WITH (nolock) ON e.EventID = re.EventID INNER JOIN
                         Reasons AS rea WITH (nolock) ON rea.ReasonID = e.ReasonID INNER JOIN
                         Users AS u WITH (nolock) ON u.UserID = e.UserID INNER JOIN
                         Recordings AS r ON r.RecordingID = re.RecordingID
WHERE        (rea.Category = 'DeleteRecording') AND (e.Occurred BETWEEN @startDate AND @endDate)
GROUP BY u.Name, rea.Text
ORDER BY Username, MAX(rea.SortOrder)

如果我限制 smallmoney 查询给出结果 30.0 的日期间隔,则上述查询有效。如果我将日期间隔扩展到 smallmoney 查询也曾经失败的时间段,我会超时。当 smallmoney 查询失败时它运行良好时,如何在 smallmoney 查询之前运行的查询中获得超时? 在 SSMS 中运行所有查询按预期工作。 顺便说一句,查询是同步运行的。

【问题讨论】:

    标签: sql-server tableadapter


    【解决方案1】:

    你能看看两者的执行计划吗? (可从下面检索)

    SELECT usecounts, cacheobjtype, objtype, text, query_plan, value as set_options
    FROM sys.dm_exec_cached_plans 
    CROSS APPLY sys.dm_exec_sql_text(plan_handle) 
    CROSS APPLY sys.dm_exec_query_plan(plan_handle) 
    cross APPLY sys.dm_exec_plan_attributes(plan_handle) AS epa
    where text like '%SELECT        CONVERT(varchar, Events.Occurred, 102) AS Day, Users.Name, COUNT(*) AS Deleted_Invoices, SUM(i.TotalExVat + i.TotalVat) AS Total%' and attribute='set_options'
    

    我想知道他们中的一个是否最终制定了一个 SUMS 记录的计划,该计划后来最终被过滤掉了。

    【讨论】:

    • 如何检查表适配器查询的执行计划?
    • @Holstebroe - 我的答案中的查询应该带回两个执行计划,只要它们在缓存中。否则,您可以运行 SQL Profiler 并将其设置为捕获执行计划。
    • 我会尝试解决我的新超时问题
    • 有趣的是,计划(我专注于我的新超时问题)看起来非常不同。应用程序计划使用大量嵌套循环,其中 SSMS 计划使用哈希匹配。那我现在该怎么办?将麻烦制造者实现为存储过程是我的最佳选择吗?
    • @Holstebroe 您可以将计划 XML 粘贴到您的问题中吗?
    【解决方案2】:

    在查询中,您是否尝试将值转换为 MONEY?

    类似

    SUM(CAST(i.TotalExVat AS MONEY) + CAST(i.TotalVat AS MONEY))
    

    看起来,当加法发生时,值保持在相同的类型,所以任何溢出都会破坏它。

    SMALLMONEY 看起来确实是一个非常小的类型。

    看看这个例子

    DECLARE @table TABLE(
            Value SMALLMONEY
    )
    
    INSERT INTO @table SELECT 200000
    INSERT INTO @table SELECT 200000
    INSERT INTO @table SELECT 200000
    INSERT INTO @table SELECT 200000
    
    --IS FINE
    SELECT SUM(CAST(Value AS MONEY) + CAST(Value AS MONEY))
    FROm    @table
    
    --BREAKS
    SELECT SUM(Value + Value)
    FROm    @table
    

    【讨论】:

    • 我显然可以投钱,它现在可能会解决症状,我已经做了一个构建,只是在部署管道中,但我在这里问是因为我不明白为什么产生 33.00 的查询给了我一个溢出。我担心溢出只是一些不同的严重错误的副作用。由于在这种情况下反复试验会耗费大量时间,因此我希望在修复之前更好地了解问题。
    【解决方案3】:

    值得检查更改 SSMS 的会话属性以匹配您的 .net 连接的属性是否可以让您复制错误。

    特别值得关注ARITHABORT,我发现它会导致与您过去描述的问题类似的问题。默认情况下,我相信 SSMS 连接将 arithabort 设置为开启,而 .net 将其关闭。

    您可以使用SET ARITHABORT [ ON | OFF] 命令更改 SSMS 中的属性。您可能需要检查探查器跟踪以确认 .Net 环境中的设置。

    【讨论】:

    • 我认为两者都默认设置ANSI_WARNINGS,但应该可以捕捉到这一点。即 ANSI_WARNINGSARITHABORT 都需要关闭才能屏蔽此错误。
    • 无论 ARITHABORT 状态如何,SSMS 仍然给我 33.00 :-( .NET 在计算 33.00 时抛出小额资金异常。
    猜你喜欢
    • 2014-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多