MSSQL | JDBI 3 | NoRowsSelect For Count Query After Update - sql-server

Java class creates a prepared statement and binds value
MS-SQL is the data store used
Jars Used
jdbi3-core-3.8.2
HikariCP: 3.4.1
mssql-jdbc:7.2.2.jre8
DECLARE #status VARCHAR(max)
DECLARE #destination VARCHAR(max) = 'xyz'
DECLARE #attributes_var NVARCHAR(max)
-- status , current_destination , container_id
UPDATE dbo.container_master
SET #status= status, status = 'DELETED', #destination= current_destination , current_destination = null , #attributes_var = JSON_QUERY(attributes , '$')
WHERE container_id = 'cont_id'
-- current_destination ,
SELECT count(container_id) AS count
FROM dbo.container_master
CROSS APPLY OPENJSON (attributes ) WITH (value NVARCHAR(100) '$')
where current_destination = #destination
and status <> 'DELETED'
AND attributes = JSON_QUERY(#attributes_var, '$')
Count Query when executed Returns No Rows , ResultSet is null
Throwing a Non-TransientExecption
Query statement = handle.createQuery(query);
this.bindParameters(statement, queryParameters);
ResultBearing queryResults = (ResultBearing)statement.execute(ResultProducers.returningResults());
results = (List)queryResults.map(this.mapMapper).stream().map(this::toBytes).collect(Collectors.toList());
Last Line Throws a NonTransient Exception
Options Tried
When Only a simple select queries are executed it works
Things Working
Update Fired.
Query executed successfully
Issue
Handling of ResultSets

Related

How to check for empty or null Guid in stored procedures

I wanted to check for null or empty GUID in stored procedure. I have used the below code:
CREATE PROCEDURE [dbo].[GetGuid]
#buildingID XML,
AS
DECLARE #fetchedBuildValue uniqueidentifier
SET #fetchedBuildValue = (SELECT data.item.value('./#ID', 'uniqueidentifier') AS PId
FROM #buildingID.nodes('/items/item') data(item));
SELECT
DemoUser.[ID],
DemoUser.[Suffix],
DemoUser.[BusinessPhoneNumber]
INNER JOIN
Relationship AS T_U ON T_U.Target = DemoUser.ID
AND T_U.TargetType = 0
AND T_U.SourceType = 6
INNER JOIN
DemoTenant ON T_U.Source = DemoTenant.ID
WHERE
(#firstname IS NULL OR DemoUser.FirstName LIKE '%' + #firstname + '%')
AND ((#fetchedBuildValue = '00000000-0000-0000-0000-000000000000'
AND Building.State = #state
AND Building.City = #city)
OR
(#fetchedBuildValue != '00000000-0000-0000-0000-000000000000'
AND Building.ID = #fetchedBuildValue))
The above stored procedure is not checking for empty guid. The guid that I am passing from code is in a xml as below:
<items><item ID="00000000-0000-0000-0000-000000000000" /></items>
However, if a valid guid is passed, it is working fine.
How can I add an empty check here so that when the guid is empty, my state and city where clause work instead of the guild column

Subquery returned more than 1 value when trigger executes

I have a trigger which adds a log entry into a table upon a field change in another table. it works when one row is changed but errors when multiple rows re changed. Anyone out there able to explain what I have to do to get my trigger working also for multi row updates?
Many thanks,
Derek
Declare #PropertyID uniqueidentifier
Set #PropertyID = (Select CONVERT(VARCHAR( 36 ), ISNULL(i.[PropertyPK], d.[PropertyPK]))
FROM
INSERTED i
FULL OUTER JOIN DELETED d ON ( d.[PropertyPK] = i.[PropertyPK] )
WHERE
( d.[strManagingOfficeName] <> i.[strManagingOfficeName] ) OR
( d.[strManagingOfficeName] IS NULL AND i.[strManagingOfficeName] IS NOT NULL ) OR
( i.[strManagingOfficeName] IS NULL AND d.[strManagingOfficeName] IS NOT NULL ))
Declare #CompanyID uniqueidentifier
Set #CompanyID = (Select CompanyFK From Property Where PropertyPK = #PropertyID)
--Deleted Old Ones
Delete From TDSAPILog Where ObjectFK = #PropertyID And strObject = 'Branch Change'
--Insert New Log
INSERT dbo.TDSAPILog(TDSAPILogPK, ObjectFK, strObject, strStatus, CompanyFK, dteDateLogged)
SELECT
NewID(),
#PropertyID,
'Branch Change',
'Active',
#CompanyID ,
GetDate()
This error occur when you return more than 1 value from a query and save in a variable or compare with a value in where clause.
In your example I think the error occur at this line
SET #CompanyID = (SELECT CompanyFK FROM Property WHERE PropertyPK = #PropertyID)
To resolve the reported error just put "TOP 1" in your query. Example is shown here:
SET #CompanyID = (SELECT TOP 1 CompanyFK FROM Property WHERE PropertyPK = #PropertyID)
Subquery returned more than 1 value error may occur at the following scenarios:
SET #YouVariable = (SELECT ColumnID FROM yourTable WHERE Identity = #SomeValue)
-- if the above query return more than 1 value the same error will be occurred
-- to resolve this problem just put "TOP 1" before ColumnID
SELECT *
FROM OtherTable
WHERE OtherIdentity = ((SELECT ColumnID FROM yourTable
WHERE Identity = #SomeValue))
-- if the above query return more than 1 value the same error will be occurred
-- to resolve this problem just replace "= with IN()". Example give below
SELECT *
FROM OtherTable
WHERE OtherIdentity IN ((SELECT ColumnID FROM yourTable
WHERE Identity = #SomeValue))

Advanced T-SQL: How Should I Update Multiple Rows While Updating Multiple Columns?

I am looking for that performance 'sweet spot' when trying to update multiple columns for multiple rows...
Background.
I work in an MDM/abstract/hierarchical classification/functional SQL Server environment. This question pertains to a post data driven calculation process where I need to save the results. I pass JSON to a SQL function that will automatically create SQL for inserts/updates (and skip updates if the values match).
The tblDestination looks like
create table tblDestination as
(
sysPrimaryKey bigint identity(1,1)
, sysHeaderId bigint -- udt_ForeignKey
, sysLevel1Id bigint -- udt_ForeignKey (classification level 1)
, strText nvarchar(100) -- from here down: the values that need to be updated
, dtmDate datetime2(7)
, numNumeric flat
, flgFlag bit
, intInteger bigint
, sysRefKey bigint-- ForeignKey
, primary key non clustered (sysPrimaryKey)
)
/* note that the clustered index on this table exists, contains more than the columns listed above, and is physically modeled correctly. you may use any clustered/IX/UI indexes that you need to if you are testing */
#JSON looks like... (ARBITRARY# ranges between 2 and 100)
declare #JSON nvarchar(max)='{"ARBITRARY NAME 1":"3/1/2017","ARBITRARY NAME 2": "Value", "ARBITRARY NAME 3": 45.3}'
The function cursors through the incoming #JSON and builds insert or update statements.
Cursor local static forward_only read_only
for
select [key] as JSON_Key, [value] from openjson(#json)
while ##fetch
-- get the id for ARBITRARY ID plus a classification id
select #level1Id = level1Id, #level2id = level2Id
from tblLevel1 where ProgrammingName = #JSON_Key
-- get a ProgrammingName field for the previously retrieved level2Id
Select #ProgrammingName = /*INTEGER/FLAG/NUMERIC/TEXT/DATE/REFKEY
from tblLevel2 where level2id = #level2id
-- clear variables
set #numeric = null, #integer = null, #text = null etc..
-- check to see if insert or update is required
Select #DestinationID from tblDestination where HeaderId = #header and Level1 = #Level1
if #DestinationId is null
begin
If #ProgrammingName = 'Numeric' begin #Numeric = #JSON_Value end
else if #ProgrammingName = 'Integer' begin #Integer = #JSON_value end
etc..
-- dynamically build the updates here..
/*
'update tblDestination
Set numeric = ' +#numeric
+', flag = '+#flag
+', date = '+#date
.. etc
+'where HeaderId = '+#header + ' and level1Id = '+#Level1Id
end
IE:
Update tblDestination
Set numNumeric = NULL
, flgFlag = NULL
, dtmDate = '3/1/2017'
Where sysPrimaryKey = 33676224
*/
Finally... to the point of this post: has anyone here had experience with multiple row updates on multiple columns?
Something like:
Set TableNumeric
= CASE WHEN Level3Id = 33676224 then null
when leve3id = 33676225 then 3.2
when level3id = 33676226 then null
end
, tableDate = case when level3id = 33676224 then '3/1/2017'
when 33676225 then null
when 33676226 then null
end
where headerId = 23897
and IDs in (33676224, 33676225, 33676226)
I know that the speed varies for Insert statements (Number of Columns inserted vs Number of records), and have that part dialed in.
I am curious to know if anyone has found that 'sweet spot' for updates.
The sweet spot meaning:
How many CASES before I should make a new update block?
Is 'Update tbl set (Column = Case (When Id = ## then ColumnValue)^n END)^n' the proper approach to reduce the number of actual Updates being fired?
Is wrapping Updates in a transaction a faster option (and how many per COMMIT)?
Legibility of the update statement is irrelevant. Nobody will actually see the code.
I have isolated the single update statement chain to be approx 70%+ of the query cost in question (compared to all inserts and 20/80,50/50,80/20 %update/%inserts)

SQL server stored procedure timeouts

The following stored procedure takes almost 2 minutes to run which is causing a time out. Both tables do have primary keys and indexes defined. Is there anyway I can improve the process time?
ALTER PROCEDURE [dbo].[dfc_rpt_validation]
-- declare
#region varchar(10) = null,
#source varchar(10) = null
AS BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
select row_number() OVER(ORDER BY l.loan_code) as Id
, l.region AS Region
, l.source AS Source
, l.loan_code_string AS CL_Loan_Code
, l.loan_principal_amt AS CL_Loan_Principal_Amt
, l.loan_amt_due AS CL_Loan_Amt_Due
, l.date_originated AS CL_Origination_Date
, l.StoreNumber AS CL_Store_Number
, v.new_loan_code_string AS FA_Loan_Code
, v.loan_principal_amt AS FA_Loan_Principal_Amt
, v.loan_amt_due AS FA_Loan_Amt_Due
, v.loan_origination_date AS FA_Origination_Date
, (select count(*) from [dbo].[dfc_LoanValidatedRecords] s WHERE s.loan_code_string = l.loan_code_string) AS FA_Times_Loan_Document_Processed
, (CASE WHEN l.rejected_date IS NULL AND l.validated_date IS NULL THEN ''
WHEN v.pdf_legible = 0 THEN 'operational reject' -- operational error
WHEN v.loan_code_valid = 1
AND v.loan_principal_amt_valid = 1
AND v.loan_origination_date_valid = 1
AND v.loan_amt_due_valid = 1
--OR v.pdf_legible = 0 -- operational error
THEN 'validated' ELSE 'rejected' END
) AS [FA_Verification_Status]
-- 100 delivery method failure
-- 200 pdf reject codes
-- 400 borrower info reject codes
-- 600 loan reject codes
, LTRIM(RTRIM (
--(CASE WHEN l.rejected_date IS NULL AND l.validated_date IS NULL THEN ''
--ELSE
(CASE WHEN v.pdf_legible = 0 THEN ' 200'
ELSE
(CASE WHEN v.loan_code_valid = 0 THEN ' 600' ELSE '' END)
+ (CASE WHEN v.loan_principal_amt_valid = 0 THEN ' 610' ELSE '' END)
+ (CASE WHEN v.loan_origination_date_valid = 0 THEN ' 620' ELSE '' END) -- LoanDate
+ (CASE WHEN v.loan_amt_due_valid = 0 THEN ' 625' ELSE '' END)
END) -- operational error
--END)
)) AS FA_Reason
, l.load_file AS load_file
from dfc_LoanRecords AS l
JOIN dfc_LoanValidatedRecords AS v ON v.loan_code_string = l.loan_code_string
WHERE CONVERT (DATE, l.load_date) >= convert(date, dateadd(hh,-8,getdate())) -- handle UTC conversion
AND l.region = #region AND l.source = #source
ORDER BY FA_Verification_Status, FA_Reason
END
Simplest change I can see:
There's no reason I can see for this: CONVERT (DATE, l.load_date).
Just use l.load_date >= convert(date, dateadd(hh,-8,getdate()))
This should enable SQL query optimiser to use any indexes that might exist on load_date. At the moment it can't do so efficiently as you're using a function on the column.
Wouldn't be better if instead of converting the Convert(date, dateadd(hh,-8,getdate()))
in the query itself to avoid that function for every record
DECLARE #Date DATE = CAST(DATEADD(HH,-8,GETDATE()) AS DATE)
WHERE CONVERT (DATE, l.load_date) >= #Date -- handle UTC conversion
AND l.region = #region AND l.source = #source
With this you are going to get a better execution plan instead of converting the value for every single row.
Also if you can apply the ORDER BY at the application layer... please read 7 Things Developers Should Know About SQL Server item 2 please do so, there's no need to use it on the database side unless we have to, it will reduce extra-cost on tempdb.

Suppress bogus result set from nested stored procedure call

As requested, edited to provide more details
I have a stored procedure (lets call it spOuter) along the following lines:
ALTER PROCEDURE [dbo].[spOuter]
(#SelFromDateUTC smalldatetime
,#SelToDateUTC smalldatetime
,#SelDriverId int = null
) AS
DECLARE #SelDriverName varchar(40)
Set Nocount on
exec dbo.spInner --<<<<<<<<<<<<<<<<<<<<<<<<<<<
Select #SelDriverName = DriverName
From dbo.tblDrivers
Where DriverID = #SelDriverId
Set Nocount off
Select #SelToDateUTC as #SelShiftDateUTC
,#SelDriverName as SelDriverName
, *
From dbo.vwRptDriverProgress
Where ActionTimeUTC between #SelFromDateUTC and #SelToDateUTC
and DriverId = coalesce(#SelDriverId, DriverId)
Order by DriverName,ActionTimeUTC,DriverLogId
When I run spInner from SSMS it does not return any result sets.
However, when I run it from spOuter I get two result sets, one from spInner and one from spOuter (commenting out the call to spInner removes the excess result set).
spInner is as follows:
ALTER PROCEDURE [dbo].[spInner] AS
set nocount on
Declare #CutoffDate smalldatetime
Set #CutoffDate = DateAdd(Month, -1, getUTCDate()) -- no need to keep reprocessing the whole table.
if #CutoffDate < '11/1/2008'
Set #CutoffDate = '11/1/2008'
Insert dbo.tblADIShifts (PU.DriverId, PU.PunchInTimeUTC, PU.PunchInLocationId)
Select DriverId, PunchInTimeUTC, PunchInLocationId
From vwUpdDriverLogsAddShifts PU -- Distinct Driver,PunchInTimeUTC combinations.
Where PU.PunchInTimeUTC > #CutoffDate
and not exists (Select *
from dbo.tblADIShifts SH
Where SH.DriverId=PU.DriverId
and SH.PunchInTimeUTc = PU.PunchInTimeUtC)
Order By PU.DriverId, PU.PunchInTimeUTC
Update dbo.tblADIShifts Set
PunchOutTimeUTC = PU.PunchOutTimeUTC
,PunchOutLocationId = PU.PunchOutLocationId
From vwUpdDriverLogsNewPunchOuts PU
Where dbo.tblADIShifts.ShiftId = PU.ShiftId and PU.PunchInTimeUTC > #CutoffDate
Insert dbo.tblDriverLogs (DriverId, ActionId, ActionTimeUTC, ShiftId, LocationId)
Select DriverId, ActionId, PunchInTimeUTC, ShiftId, PunchInLocationId
From dbo.vwUPDDriverLogsSP
Insert dbo.tblDriverLogs (DriverId, ActionId, ActionTimeUTC, ShiftId, LocationId)
Select DriverId, ActionId, PunchOutTimeUTC, ShiftId, PunchOutLocationId
From dbo.vwUPDDriverLogsFP
Update dbo.tblDriverLogs Set
ShiftId = SH.ShiftId
From dbo.vwUpdDriverLogsAssignShifts SH
Where SH.PunchInTimeUTC > #CutoffDate
and dbo.tblDriverLogs.DriverLogId = SH.DriverLogId
Update dbo.tblDriverLogs Set
ShiftId = PrevShiftId
From dbo.vwUpdDriverLogsShiftless3 SH
Where dbo.tblDriverLogs.DriverLogId = SH.DriverLogId
--<<<<<<<<< The bogus (and empty) result set has the columns
--<<<<<<<<< of tblMovementLocations which is only referenced here:
Update dbo.tblMovementLocations Set
Distance = CalcDistance
From vwExcessiveOrderDistances vw
Where dbo.tblMovementLocations.MovementLocationId = vw.MovementLocationId
Update dbo.tblDriverLogs Set
DriveTimeMinutes = VW.DriveTimeMinutes
,BreakTimeMinutes = VW.BreakTimeMinutes
,DelayTimeMinutes = VW.DelayTimeMinutes
,LocationId = VW.LocationId
From dbo.vwUpdDriverLogs VW
Where dbo.tblDriverLogs.DriverLogId = VW.DriverLogId
and VW.ActionTimeUTC > #CutoffDate
and (dbo.tblDriverLogs.DriveTimeMinutes <> VW.DriveTimeMinutes
or dbo.tblDriverLogs.BreakTimeMinutes <> VW.BreakTimeMinutes
or dbo.tblDriverLogs.DelayTimeMinutes <> VW.DelayTimeMinutes
or coalesce(dbo.tblDriverLogs.LocationId,-1) <> VW.LocationId)
Surely, these selects are part of the insert/update statements and should not return result sets?
Is there any way around this?
SQLServer 2005 SP2 Version 9.00.4035.00
Perhaps changing the syntax of your update statement(s) in spInner to use an alias (see Good way to use table alias in Update statement?) and a JOIN might change the behavior - something like this, for example:
UPDATE [locations]
SET Distance = CalcDistance
FROM dbo.tblMovementLocations AS [locations]
INNER JOIN vwExcessiveOrderDistances AS [vw]
ON [locations].MovementLocationId = [vw].MovementLocationId;
The thought process being that possibly there aren't any corresponding record(s) in vwExcessiveOrderDistances to be matched with in dbo.tblMovementLocations, and maybe this is causing the database engine to return an empty result set by treating the statement like a SELECT statement on dbo.tblMovementLocations. It would certainly be strange if this is the case!
This is just my speculation though...

Resources