【发布时间】:2019-01-09 16:10:47
【问题描述】:
因此,我一直在为我正在构建的 SSRS 报告处理多个存储过程,但遇到了一个奇怪的错误,需要一双新的眼睛来看看我可能遗漏了什么。
我的程序非常简单 - SELECT 来自一些 JOINed 表的各种列,INSERT 将它们转换为 #temp 表和 SELECT 表的所有内容,以在我的表中显示为详细行报告。
我的完整过程如下所示:
USE [DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[rpt_select_ACHW_Ob]
@PR_ID INT
AS BEGIN
SET NOCOUNT ON;
CREATE TABLE #temp
(BuildingNum INT,
MTHD VARCHAR(50),
ObDescript NVARCHAR(50),
SizeRemarks VARCHAR(50),
ObLength NUMERIC,
ObWidth NUMERIC,
ObArea NUMERIC,
Stories NUMERIC,
Grade VARCHAR(50),
YearBuilt SMALLINT,
Condition VARCHAR(50),
Phys NUMERIC,
FC VARCHAR(50),
PercentDone NUMERIC,
TaxValue NUMERIC)
DECLARE @RowCounter INT, @h INT
SET @RowCounter = 11
INSERT INTO #temp(BuildingNum, MTHD, ObDescript, SizeRemarks, ObLength, ObWidth, ObArea, Stories, Grade, YearBuilt, Condition, Phys, FC, PercentDone, TaxValue)
SELECT ob.Ob_LineNumber AS BuildingNum,
CASE WHEN ob.SP IS NOT NULL THEN 'S' ELSE 'P' END AS MTHD,
som.Id AS ObDescript,
CASE WHEN ob.SF IS NULL THEN ob.CN ELSE CAST((ob.SF + '/' + ob.CN) AS VARCHAR) END AS SizeRemarks,
CASE WHEN ob.ob_Length IS NULL THEN 0 ELSE ob.Ob_Length END AS ObLength,
CASE WHEN ob.ob_Width IS NULL THEN 0 ELSE ob.Ob_Width END AS ObWidth,
CASE WHEN ob.ob_Length IS NULL WHEN ob.ob_Width IS NULL THEN 0 THEN 0 ELSE (ob.Ob_Length * ob.Ob_Width) END AS ObArea,
ob.Ob_NStories AS OBStories,
sovg.Grade AS ObGrade,
ob.Ob_YearBuilt As ObYearBuilt,
ob.Ob_ConditionCode AS ObConditionCode,
ob.DR AS phys,
ob.FC AS FC,
ob.Ob_PercentComplete AS ObPercentComplete,
ob.Ob_ValueTax AS TaxValue
FROM t_Ob ob WITH (NOLOCK)
LEFT JOIN t_ObToPR otpr WITH (NOLOCK) ON ob.Ob_ID=otpr.Ob_ID
LEFT JOIN t_PR pr WITH (NOLOCK) ON otpr.PR_ID=pr.PR_ID
LEFT JOIN t_S_Grade sovg WITH (NOLOCK) ON ob.S_Grade_ID=sovg.S_Grade_ID
LEFT JOIN t_SObD sod WITH (NOLOCK) ON ob.SObD_ID=sod.SObD_ID
LEFT JOIN t_SObM som WITH (NOLOCK) ON sod.SObM_ID=som.SObM_ID
WHERE pr.PR_Id = @PR_ID
SET @h = (SELECT COUNT(*) FROM #temp)
WHILE @h < @RowCounter OR @h % @RowCounter > 0
BEGIN
INSERT INTO #temp (BuildingNum) VALUES (NULL)
SET @h = @h + 1
END
SELECT * FROM #temp
ORDER BY CASE WHEN BuildingNum IS NULL THEN 1 ELSE 0 END, BuildingNum
END
正如我所说,我一直对这段代码有一个奇怪的问题。在过去的两周里,所有测试用例都运行良好。我正在使用EXEC 根据参数@PR_ID 选择记录,它工作正常。昨天,在没有接触任何代码之后,我开始为某些 PR_ID 值生成错误代码:
消息 8114,级别 16,状态 5,过程 rpt_select_ACHW_Ob,第 28 行 [批处理开始第 2 行]
将数据类型 varchar 转换为数值时出错。
第 28 行将您带到FC VARCHAR(50),我已经检查了 10 次。 #temp 表中声明的所有数据类型都与选择的值完全匹配。有没有人知道为什么这停止工作?
Here's a dbFiddle link with some sample data
目前正在使用 SQL Server 2012。
【问题讨论】:
-
你为什么要在这样的循环中插入?我看不出您提供的“示例数据”与您的查询有什么关系。您提供了一个表,您的查询中有几个。另外,小心将 NOLOCK 溅到各处。它不是一个性能工具。除了许多其他“功能”之外,它还可以并且将返回丢失和/或重复的行。 blogs.sentryone.com/aaronbertrand/bad-habits-nolock-everywhere
-
我认为您的选择部分(插入查询)中的 case-when 语句会导致这些情况。请注释掉 case-when 语句并重试(改用 null)。如果它有效,请找出是哪一个导致它,然后查看该特定列的所有值。
-
@SeanLange 报告需要特定的格式,其他 procs 的常见做法是简单地插入
NULL值以填充报告上所需的空间。NOLOCK也是如此。可能有更简单的方法,但我是一家成熟公司的入门级开发人员,所以我有什么资格质疑他们的方法?样本数据只是我期望从表中返回的样本。t_ob和t_pr都包含 30 列与财产记录相关的各种数据。 -
对于你自己,你应该考虑使用一个计数表而不是一个循环来插入一堆这样的行。如此简单和快捷。这是一篇很棒的文章,解释了它如何替换循环。 sqlservercentral.com/articles/T-SQL/62867 至于 NOLOCK……嗯……这是一种糟糕的做法,但有时我们会被卡住。请记住,它不是免费的性能工具。在这种情况下,成本是准确性。有些时候还可以,而其他时候则不然。对于手头的问题,请参阅 Gordon 的回答。
标签: sql sql-server