I have an ASPX page using VB.net code, and it is set up to run a series of SQL queries and populate a SQLDataReader with the results. Each SQL query is pulled from a specific field in a table within the SQL database. This code works perfectly for literally every query I run through it, except one.
The VB code involved is as follows.
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
As you can see, I have code in place to catch an error, so I know exactly where the error is happening, but I'm not sure why. The error occurs when
SQL_Template_Query = "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; "
I'm providing the string as is, in case anyone wants to know how VB.net is parsing it. The more developer-friendly parsing of this query looks like this.
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;
When I run this query in SQL Server Management Studio, character for character, it returns exactly the results I expect.
However, when the VB code runs, it triggers the catch code. Having read posts for similar problems, I tried modifying the query to yield 0 instead of NULL, but the issue persists. Also, other queries yield NULL values but work fine with the VB code.
Any advice would be appreciated. I would also like to know if there's a way to get SQLDataAdapter or SQLCommand to return the error. It would make things much easier to diagnose.
UPDATE: If I comment out the Try...Catch...End Try, and allow Visual Studio to generate an error, I receive the following.
Global_Objects.SQLLocalDB_Connection
is one connection object that is used throughout, that is the wrong way to use a connection. The correct way is to instantiate the connection, use it, then call.Dispose()
on it. – Andrew Morton