SQL 查询字符串在 SQL Server Management Studio 中有效,但在带有 SQLCommand.ExecuteReader 的 VB.net 中无效

Posted

技术标签:

【中文标题】SQL 查询字符串在 SQL Server Management Studio 中有效,但在带有 SQLCommand.ExecuteReader 的 VB.net 中无效【英文标题】:SQL Query String Works in SQL Server Management Studio, But Not in VB.net with SQLCommand.ExecuteReader 【发布时间】:2020-06-06 09:25:10 【问题描述】:

我有一个使用 VB.net 代码的 ASPX 页面,它被设置为运行一系列 SQL 查询并使用结果填充 SQLDataReader。每个 SQL 查询都是从 SQL 数据库中的表中的特定字段中提取的。这段代码几乎适用于我通过它运行的每个查询,除了一个。

涉及的VB代码如下。

Dim SQL_Template_Query As String
Dim cmd3 As SqlCommand = New SqlCommand
Dim r3 As SqlDataReader    

cmd3 = New SqlCommand(SQL_Template_Query)
cmd3.CommandTimeout = 1200 ' Extended to 1200 sec = 20 min
cmd3.CommandType = CommandType.Text
cmd3.Connection = Global_Objects.SQLLocalDB_Connection
Try
    r3 = Nothing
    r3 = cmd3.ExecuteReader() ' THIS LINE IS WHERE THE ERROR OCCURS
Catch
    Session("Main_Error_Code") = 1
    Session("Main_Error_Message") = "The SQL statement for the following address is invalid. Please examine the GCDB_Excel_Reports_Templates record, and correct." &
                    Chr(13) & "ID " & ti.ID & ", " & ti.Sheet_Name & ", " & ti.Cell_Address

    ' Write to error log
    cxlErrorsSheetLog.Cell("A" & cxlErrorsCurRow).Value = Session("Main_Error_Message")
    cxlErrors.SaveAs(cxlErrorsPath)
    cxlErrors = Nothing
    cxlDoc = Nothing
    Return
End Try

如您所见,我有代码可以捕获错误,所以我知道错误发生的确切位置,但我不确定原因。发生错误时

SQL_Template_Query = "DECLARE @Start_Year int;SET @Start_Year=2018;DECLARE @Reporting_Year int;SET @Reporting_Year=2021;声明 @First_Year int;设置 @First_Year = ( case when (@Reporting_Year-@Start_Year+ 1)=@First_Year or End_Date is null) 按 Building_ID 分组;选择 i1.Reporting _Year,SUM(当 i2.Asset_Class 像 'Office' 然后 GFA else null end 的情况)作为 Office_GFA,SUM(当 i2.Asset_Class 像 'Retail' 然后 GFA else null end 的情况)作为 Retail_GFA,SUM(当 i2.Asset_Class 像这样的情况'Industrial' then GFA else null end) as Industrial_GFA from #Reporting_Years i1 left join ( select z1.Reporting_Year,x.Building_ID,y.Building,y.Asset_Class,x.Start_Year,x.End_Year,y.Most_Recent_GFA_Year,z1 时的情况.Reporting_Year0 或 e .Building_Count 为空)按 a.Building_ID,a.Building,a.Asset_Class) y 在 x.Building_ID=y.Building_ID 上交叉连接 #Reporting_Years z1 其中 z1.Reporting_Year 在 @First_Year 和 @Reporting_Year 之间z1.Reporting_Year>=x.Start_Year and z1.Reporting_Year

我按原样提供字符串,以防有人想知道 VB.net 是如何解析它的。此查询的开发人员更友好的解析如下所示。

DECLARE @Start_Year int;
SET @Start_Year=2018;
DECLARE @Reporting_Year int;
SET @Reporting_Year=2021;

declare @First_Year int;
set @First_Year =  
(  
case   
when (@Reporting_Year-@Start_Year+1)<=5 then @Start_Year   
else @Reporting_Year - 4 end  
);       

drop table if exists #Reporting_Years;       

create table #Reporting_Years  
(
Reporting_Year int
);   

insert into #Reporting_Years 
select distinct YEAR(Single_Date) 
from Single_Dates where YEAR(Single_Date) 
between @Reporting_Year-4 and @Reporting_Year;   

drop table if exists #Has_ACT;   

create table #Has_ACT (Building_ID int,Start_Date datetime,End_Date datetime);   

insert into #Has_ACT  
select distinct Building_ID,MIN(Start_Date),MAX(End_Date)  from Tracking_Periods  
where Utility_Type_ID between 16 and 19 
and YEAR(Start_Date)<=@Reporting_Year  
and (YEAR(End_Date)>=@First_Year or End_Date is null)  
group by Building_ID;     

select i1.Reporting_Year,  
SUM(case when i2.Asset_Class like 'Office' then GFA else null end) as Office_GFA,  
SUM(case when i2.Asset_Class like 'Retail' then GFA else null end) as Retail_GFA,  
SUM(case when i2.Asset_Class like 'Industrial' then GFA else null end) as Industrial_GFA  
from #Reporting_Years i1  
left join
(
select z1.Reporting_Year,x.Building_ID,y.Building,y.Asset_Class,x.Start_Year,x.End_Year,y.Most_Recent_GFA_Year,  
case  when z1.Reporting_Year<=y.Most_Recent_GFA_Year   
then 
(select GFA from Annual_GFA where Building_ID=x.Building_ID and Reporting_Year=z1.Reporting_Year)  
else 
(select GFA from Annual_GFA where Building_ID=x.Building_ID and Reporting_Year=y.Most_Recent_GFA_Year)
end as GFA  
from   
(  
select Building_ID,  
case  
when YEAR(Start_Date)<@First_Year then @First_Year  
else YEAR(Start_Date) end as Start_Year,  
case  
when End_Date is null then @Reporting_Year  
when YEAR(End_Date)>@Reporting_Year then @Reporting_Year  
else YEAR(End_Date) end as End_Year from #Has_ACT) x  
left join   
(
select a.Building_ID,a.Building,a.Asset_Class,MAX(a.Reporting_Year) as Most_Recent_GFA_Year  
from View_All_Buildings_Annual_GFA a      
inner join View_All_Buildings b on a.Building_ID=b.Building_ID      
inner join View_All_Building_Ownership_By_Year c on c.Building_ID=a.Building_ID and c.Reporting_Year=@Reporting_Year      
inner join Building_Ownership d on a.Building_ID=d.Building_ID      
left join Override_Building_Count e on a.Building_ID=e.Building_ID and e.Report_ID=5      
inner join #Has_ACT f on f.Building_ID=a.Building_ID    
where b.Asset_Manager like 'Anonymous'      
and b.Not_On_Program=0 and b.Exclude_From_Reporting=0 and b.Tenant=0      
and d.Year_Removed is null    
and (e.Building_Count<>0 or e.Building_Count is null)  
group by a.Building_ID,a.Building,a.Asset_Class) y on x.Building_ID=y.Building_ID  
cross join #Reporting_Years z1 where z1.Reporting_Year between @First_Year and @Reporting_Year  
and z1.Reporting_Year>=x.Start_Year and z1.Reporting_Year<=x.End_Year  
and y.Building_ID is not null) i2 on i1.Reporting_Year=i2.Reporting_Year  
where i1.Reporting_Year between @Start_Year and @Reporting_Year  
group by i1.Reporting_Year  
order by i1.Reporting_Year;  

当我在 SQL Server Management Studio 中逐个字符地运行此查询时,它返回的结果完全符合我的预期。

但是,当 VB 代码运行时,它会触发 catch 代码。在阅读了类似问题的帖子后,我尝试修改查询以产生 0 而不是 NULL,但问题仍然存在。此外,其他查询会产生 NULL 值,但可以在 VB 代码中正常工作。

任何建议将不胜感激。我还想知道是否有办法让 SQLDataAdapter 或 SQLCommand 返回错误。这将使事情更容易诊断。

更新:如果我注释掉 Try...Catch...End Try,并允许 Visual Studio 生成错误,我会收到以下信息。

【问题讨论】:

你考虑过使用存储过程吗? 您是否尝试过“catch ex”以便获取异常并打印出异常的文本(ex.Message)? @Grantly 我对存储过程的理解是,创建一个,需要事先声明参数。上面查询中显示的前两个参数实际上是在代码运行时动态创建的,并且因模板而异。与大部分 SQL 查询本身非常相似,它们是在运行时从表中提取的。因此,在代码实际运行之前,它们都是未知数。 @J.P.Brown 顺便说一句,如果Global_Objects.SQLLocalDB_Connection 是一个自始至终使用的连接对象,那么使用连接的方式是错误的。正确的方法是实例化连接,使用它,然后调用.Dispose()就可以了。 @J.P.Brown 如果您将 SQL 发布到t-sql-beautilyzer 并使用“分析”按钮,它会显示一些可能有用的建议。 【参考方案1】:

因此,对于使用单个连接的多个 SQL 查询,@AndrewMorton 似乎走在了正确的轨道上。我创建了一个全新的 VB.net 项目,添加了代码以提取导致问题的特定存储 SQL 语句,然后运行它。它没有问题。知道,在原始项目中,我替换了代码以在单独的连接上运行特定的 SqlCommand,在每个循环之后处理连接实例。

代码现在按预期工作。目前尚不清楚为什么这个特定的 SQL 语句会导致问题,而其他的则不会。但是,无论如何,我将重新编写此项目中的所有代码以移出单个连接对象。

@AndrewMorton 的其他评论 https://ubitsoft.com/t-sql-beautilyzer/ 中提供的链接也很有帮助。实际上,我一直在寻找一种无需底层数据库即可独立检查 SQL 查询的方法。

【讨论】:

以上是关于SQL 查询字符串在 SQL Server Management Studio 中有效,但在带有 SQLCommand.ExecuteReader 的 VB.net 中无效的主要内容,如果未能解决你的问题,请参考以下文章

sql server 日期范围查询

sql server 查询字符串指定字符出现的次数

SQL 查询字符串在 SQL Server Management Studio 中有效,但在带有 SQLCommand.ExecuteReader 的 VB.net 中无效

Sql Server中通配符

如何在 SQL Server 中进行包含字符串每个单词的搜索查询?

sql server 查询表 in一个数组