AFTER UPDATE TRIGGER with Conditional Replies - sql-server

I've written an After Update Trigger on tblVisitLog when a specific field, TimeOUT gets updated a new record is INSERTED in tblClinicalDoc. There is a possibility that a record with an identifying ID (Encounter_code) is already in the Target Table, so I added an IF EXIST and an IF NOT EXISTS for the UPDATE or INSERT statements.
I'm sure there must be a more elegant and efficient way of writing this trigger than what I have. Here is what I have:
USE [test_db1]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[CreateRowInClinicalDoc]
ON [dbo].[tblVisitLog]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF UPDATE(TimeOUT)
IF EXISTS (SELECT c.Encounter_code FROM tblClinicalDoc c
INNER JOIN INSERTED i ON c.Encounter_code = i.Encounter_code
WHERE I.TimeOut IS NOT NULL)
UPDATE tblClinicalDoc
SET EditDate = GetDate()
FROM INSERTED i
INNER JOIN tblClinicalDoc c ON c.Encounter_code = i.Encounter_code
END
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS (SELECT c.Encounter_code FROM tblClinicalDoc c
INNER JOIN INSERTED i ON c.Encounter_code = i.Encounter_code
WHERE I.TimeOut IS NOT NULL)
INSERT INTO tblClinicalDoc
SELECT DISTINCT v.CaseNumber AS CaseNumber, v.Episode AS Episode, v.Encounter_code AS Encounter_code, v.Provider AS Provider,
(r.Discipline + '_'+ r.ReportType) AS Note_Type, (r.Discipline + '_' +r.ReportType + ' - ' + v.Case_Name + ' ' + convert(nvarchar(10),
v.TimeIN, 112)) AS Note_Synopsis,
(UPPER(w.ClientCode) + '_' + v.Discipline + '_' + v.VisitType + '_' + RIGHT('0000000' + CONVERT(VARCHAR,v.Encounter_code), 7)+ '_' + convert(nvarchar(10), v.TimeIN, 112) + '.pdf') AS Document_Name,
GetDate() AS EditDate, v.Provider AS EditBy, '' AS InterfaceDate, '' AS InterfaceAgent, '-2' AS Sent, 0 AS Ack, '' AS Document_ID, '' AS Confired
FROM inserted v
INNER JOIN [WebLoginSelector].[dbo].LookupDatabaseCode w ON DB_NAME() = w.ClientDatabaseCode
INNER JOIN LookupReportTypes r ON v.Discipline = r.Discipline AND v.VisitType = r.VT
WHERE v.TimeOUT IS NOT NULL
END
GO
Thanks in advance for all replies,
JackW9653

Related

SQL query optimization using dynamic query

I have the following stored procedure. It uses a common table expression and a table valued function. It takes 600 ms to execute this sp. I used dynamic query to avoid the use of a table valued function and the the execution time reduced to around 300 ms. Is it possible to further tune this select query so that the execution time comes down to around 100 ms.
--EXEC Sp_GetTripListByBusinesses 1,2,3,4,5,6,7,8,9,10,11,12,13,14,93', 1, 3, 1
CREATE PROCEDURE [dbo].[Sp_GetTripListByBusinesses]
(
#BusinessID varchar (max),
#TripFlag int,
#TripFrom INT,
#IsAllTrip INT
)
AS
BEGIN
DECLARE #Query nvarchar(max) = '';
SET #Query= '
;With CTE
AS
(
select TripID, count(TripNotesID) as NoteCount from tripnotes WHERE
TripNoteTypeID != 13 group by TripID
)
SELECT TRP.[TripID]
,isnull(TRP.[AssignedTruckerID],0) as AssignedTruckerID
,TRP.[BusinessID] AS BusinessID
,isnull(TRP.[TruckRate],''0.0'') as TruckRate
,TRP.[RequestedDatetime]
,TRP.[ArrivedDatetime]
,TRP.[DepartedDatetime]
,TRP.[TripStatusID]
,isnull(TRP.[IsDelayed] ,0) as IsDelayed
,isnull(TRP.[IsStopped] ,0) as IsStopped
,isnull(TRP.[RepeatUntilStop],'''') AS RepeatUntilStop
,TRP.[ArrivedAtBusinessDatetime]
,TRP.[OrgSiteID]
,TRP.[OriginCustomerID] AS OrgCustomerID
,OS.[SiteName] + '' / '' + ISNULL(OS.[AS400_ID],'''') AS OrgSiteName
,isnull(OS.[SiteLat],''0.0'') as OrgSiteLat
,isnull(OS.[SiteLong],''0.0'') as OrgSiteLong
,isnull(OS.[WaypointsAvl],0) as OrgWaypointsAvl
,isnull(OS.[NearestRoadwayLat],''0.0'') as OrgNearestRoadwayLat
,isnull(OS.[NearestRoadwayLong],''0.0'') as OrgNearestRoadwayLong
,OS.[SiteStatus] AS OrgSiteStatus
,OS.[AS400_ID] AS OrgAS400_ID
,TRP.[DestSiteID]
,TRP.[DestCustomerID] AS DestCustomerID
,DS.[SiteName] + '' / '' + ISNULL(DS.[AS400_ID],'''') AS DestSiteName
,isnull(DS.[SiteLat],''0.0'') as DestSiteLat
,isnull(DS.[SiteLong],''0.0'') as DestSiteLong
,isnull(TRK.FirstName,'''') + '' '' + ISNULL(TRK.[LastName],'''') AS
TruckerName
,isnull(TRK.[TruckType],''A'') as TruckType
,TRK.[Mobile] AS TruckerMobile
,B.[Name] AS BusinessName
,isnull(B.BusinessLat,''0.0'') as BusinessLat
,isnull(B.BusinessLong,''0.0'') as BusinessLong
,B.ContactNo AS BusinessContactNo
,B.Address1 AS BusinessAddress1
,B.Address2 AS BusinessAddress2
,B.City AS BusinessCity
,B.[State] AS BusinessState
,B.Zip AS BusinessZip
,TS.[StatusText]
,CASE WHEN TA.[BillOfLading] IS NULL THEN ''NA'' ELSE ''BOL'' END AS
BillOfLading
,CASE WHEN TA.[ScaleTicket] IS NULL THEN ''NA'' ELSE ''ST'' END AS
ScaleTicket
,CASE WHEN TA.[OriginScaleTicket] IS NULL THEN ''NA'' ELSE ''OST'' END AS
OriginScaleTicket
,TRP.OriginGrossBushels
,TRP.DestGrossBushels
,(SELECT count(TripNotesID) FROM dbo.[TripNotes]) as Notes
,TRP.MainTripID
,Convert(DATE, TRP.[RequestedDatetime]) AS TripDate
,1 as TripOrderNo
,isnull(TRP.IsSiteDelayed ,0) as IsSiteDelayed
,TRP.CommodityID
,C.CommodityName
,CTE.NoteCount
,TRP.InvoiceId
,TRP.OneWayMiles as OneWayMile
,OS.SiteType AS OriginSiteType
,DS.SiteType AS DestSiteType
FROM dbo.[Trip] AS TRP
INNER JOIN dbo.[Site] AS OS ON TRP.OrgSiteID = OS.SiteID
INNER JOIN dbo.[Site] AS DS ON TRP.DestSiteID = DS.SiteID
LEFT JOIN dbo.[Trucker] AS TRK ON TRP.AssignedTruckerID = TRK.TruckerID AND ISNULL(TRK.TruckerStatus,''A'') = ''A''
LEFT JOIN dbo.[Commodity] as C ON TRP.CommodityID = C.CommodityID AND C.RecordStatus = ''A''
INNER JOIN dbo.[Business] AS B ON B.BusinessID = TRP.BusinessID
LEFT JOIN dbo.[TripStatus] AS TS ON TS.TripStatusID = TRP.TripStatusID
LEFT JOIN dbo.[TripAsset] AS TA ON TA.TripID = TRP.TripID
LEFT JOIN CTE ON CTE.TripID = TRP.TripID
WHERE
TRP.RecordStatus=''A'' and
TRP.BusinessID in (' +#BusinessID + ')
ORDER BY TRP.RequestedDatetime ASC';
execute sp_executesql #Query;
END

Paginating a parent in SQL Server on a parent/child query

(SQL Server 2012 - Web Edition)
I have a parent/child (one to many) relationship in a query like so:
SELECT a.a, a.b, b.c
FROM tablea INNER JOIN
tableb ON b.pk = a.fk
I have a huge pagination query that encompasses this using the standard (psuedo-code):
WITH C as (SELECT top(#perpage*#pagenum) rowID = row_number() OVER (somefield)),
SELECT c.* FROM C (query) WHERE DT_RowId > (#pagenum-1)*#perpage
The question I have is in this scenario is it possible to paginate off the parent table (a), instead of the entire query? Can I modify my pagination query (not the sql that pulls the query itself) so that when I ask for 10 rows, it gives me 10 rows from the parent, with 'x' number of children attached?
I know I'm not giving the bigger picture here, but the bigger picture is ugly. If need be, we can go there, but it's out there. Here's a small taste of where we're going with this:
IF UPPER(LEFT(#rSQL, 6)) = 'SELECT'
BEGIN
SET #rSQL = 'SELECT * FROM (' + #rSQL + ')' + ' as rTBL';
SET #rSQL = RIGHT(#rSQL, LEN(#rSQL)-7);
IF (LEN(LTRIM(#search)) > 0)
BEGIN
SET #rPaging =
'IF (#schemaonly=1) SET FMTONLY ON;
SELECT #ttlrows = COUNT(*) FROM (SELECT ' + #rSQL + #rWhere + ') AS TBL;
WITH C as (select top(#perpage*#pagenum) DT_RowId = ROW_NUMBER() OVER (' + #rOrder + '), ';
SET #rPaging = #rPaging + #rSQL + #rWhere + ')
SELECT C.*' + #rcols + ', (#perpage-1) * #pagenum as pagenum, #ttlrows as ct, CEILING(#ttlrows / CAST(#perpage AS FLOAT)) as pages
FROM C '+ #query + ' WHERE DT_RowId > (#pagenum-1) * #perpage ';
END
ELSE
BEGIN
SET #rPaging =
'IF (#schemaonly=1) SET FMTONLY ON;
SELECT #ttlrows = COUNT(*) FROM (' + #oSQL + ') AS SUBQUERY;
WITH C as (select top(#perpage*#pagenum) DT_RowId = ROW_NUMBER() OVER (' + #rOrder + '), ';
SET #rPaging = #rPaging + #rSQL + ')
SELECT C.*' + #rcols + ',(#perpage-1) * #pagenum as pagenum, #ttlrows as ct, CEILING(#ttlrows / CAST(#perpage AS FLOAT)) as pages
FROM C '+ #query + ' WHERE DT_RowId > (#pagenum-1) * #perpage ';
END
PRINT #rPaging;
EXECUTE SP_EXECUTESQL #rPaging, #parms, #ttlrows out, #schemaonly, #perpage, #pagenum, #fksiteID, #filter1, #filter2, #filter3, #filter4, #intfilter1, #intfilter2, #intfilter3, #intfilter4, #datefilter1, #datefilter2, #search;
SET FMTONLY OFF;
END
ELSE
BEGIN
SET #rSQL = LTRIM(REPLACE(UPPER(#rSQL), 'EXEC',''));
EXECUTE SP_EXECUTESQL #rSQL, #parms, #ttlrows out, #schemaonly, #perpage, #pagenum, #fksiteID, #filter1, #filter2, #filter3, #filter4, #intfilter1, #intfilter2, #intfilter3, #intfilter4, #datefilter1, #datefilter2;
END
You could do the pagination in a CTE that only gets the parent rows, and then join the child rows in a subsequent CTE or in the main query.
Due to the dynamic way you are using this, this might have to involve building your pagination query from the same building blocks you use to build #query. Without seeing the code that builds #query I can't be much more specific than that.
You could add
,DENSE_RANK() OVER (ORDER BY table_a.primary_key)
This would indirectly provide the same result as
,ROW_NUMBER() OVER(ORDER BY table_a.primary_key)
but the former would be on the final result set instead going back to table a for the latter code snippet.
But please be aware of the disadvantage: any additional ranking function will force an additional sort operation on the result set! This might significantly influence the query performance. If this is the case in your scenario, I'd recommend to follow Tab Allemans solution and use a cte.

Get name from variable using index in T-SQL

Using the following two queries
Query 1:
DECLARE #ContentColumnNamesSRC NVARCHAR(4000) = NULL,
SELECT
#ContentColumnNamesSRC = COALESCE(#ContentColumnNamesSRC + ', ', '') + '[' + name + ']'
FROM
tempdb.sys.columns
WHERE
1 = 1
AND object_id = OBJECT_ID('tempdb..#tempTable')
AND column_id < 9 -- First 8 columns are ID data, which is what I am after
Query 2:
DECLARE #ContentColumnNamesDST NVARCHAR(4000) = NULL,
SELECT
#ContentColumnNamesDST = COALESCE(#ContentColumnNamesDST + ', ', '') + '[' + name + ']'
FROM
tempdb.sys.columns
WHERE
1 = 1
AND object_id = OBJECT_ID('Import.dbo.ContentTable')
AND column_id < 9 -- First 8 columns are ID data, which is what I am after
I can get the first 8 columns from each table into a variable.
What I would like to do is find a way to get the values out of the variable, such that I can match the column names.
They should be identical in each table, and I need it to be able to create a dynamic merge statement, such that the columnsnames from each variable
#ContentColumnNamesSRC
and
#ContentColumnNamesDST
line up, so I can use it in a merge statement.
The point of this is to be able to use it in a loop, and all i would have to do is change which tables it looks at and the merge statements would still work.
Ideally, id like to end up with something like the following:
SELECT #StageSQLCore = N'USE Staging;
BEGIN TRANSACTION
MERGE '+#StageTableCore+' AS DST
USING '+#ImportTableCore+' AS SRC
ON (SRC.[Key] = DST.[Key])
WHEN NOT MATCHED THEN
INSERT ('+#StageTableCoreColumns+')
VALUES (
'+#ImportTableCoreColumns+',GETDATE())
WHEN MATCHED
THEN UPDATE
SET
DST.'+#ContentColumnNamesDST[i]' = SRC.'+#ContentColumnNamesSRC[i] +'
,DST.'+#ContentColumnNamesDST[i]' = SRC.'+#ContentColumnNamesSRC[i] +'
,DST.'+#ContentColumnNamesDST[i]' = SRC.'+#ContentColumnNamesSRC[i] +'
,DST.'+#ContentColumnNamesDST[i]' = SRC.'+#ContentColumnNamesSRC[i] +'
,DST.[ETLDate] = GETDATE()
;
COMMIT'
EXEC (#StageSQLCore)
You can generate Merge SQL like this if both the ordinal are matching
DECLARE #MergeSQL NVARCHAR(4000) = NULL
SELECT --*--,
#MergeSQL = COALESCE(#MergeSQL + ', DST.=', '') + QUOTENAME(bc.column_name) + ' = SRC.' + QUOTENAME(bc.COLUMN_NAME) + char(13)
FROM
test.INFORMATION_SCHEMA.COLUMNS tc
inner join testb.INFORMATION_SCHEMA.COLUMNS bc
on tc.TABLE_NAME = bc.TABLE_NAME
and tc.ORDINAL_POSITION = bc.ORDINAL_POSITION
and tc.TABLE_NAME = 'History'
WHERE
tc.ORDINAL_POSITION < 5 -- First 8 columns are ID data, which is what I am after
and bc.ORDINAL_POSITION < 5
select #MergeSQL

SQL Server 2012 - Manipulate string data for stored procedure

Can you give me some pointers (or point in the right direction on what search terms for google)? In a stored procedure I have a parameter #TAG (string). I receive '(14038314,14040071)' (for example) from another application that cannot be altered. In the stored procedure, I need to split apart '(14038314,14040071)' to put quotes around each string value, rebuild it, strip out the outer quotes,strip out the parens and pass it to #TAG in the query below so that it looks like the line commented out below?
SELECT
V.NAME AS VARIETY, TAGID
FROM
mfinv.dbo.onhand h
INNER JOIN
mfinv.dbo.onhand_tags t on h.onhand_id = t.onhand_id
INNER JOIN
mfinv.dbo.onhand_tag_details d on t.onhand_tag_id = d.onhand_tag_id
INNER JOIN
mfinv.dbo.FM_IC_PS_VARIETY V ON V.VARIETYIDX = d.VARIETYIDX
LEFT JOIN
mfinv.dbo.FM_IC_TAG TG ON TG.TAGIDX = t.TAGIDX
WHERE
h.onhand_id = (SELECT onhand_id FROM mfinv.dbo.onhand
WHERE onhand_id = IDENT_CURRENT('mfinv.dbo.onhand'))
AND TG.ID IN (#TAG)
--AND TG.ID IN ('14038314','14040071')
You can Use Dynamic SQL Like This
DECLARE #TAG Nvarchar(MAX)='14038314,14040071'
set #TAG=''''+REPLACE(#TAG,',',''',''')+''''
--select #TAG
DECLARE #SQL NVARCHAR(MAX)=N'
Select V.NAME AS VARIETY, TAGID
FROM mfinv.dbo.onhand h
INNER JOIN mfinv.dbo.onhand_tags t on h.onhand_id = t.onhand_id
INNER JOIN mfinv.dbo.onhand_tag_details d on t.onhand_tag_id = d.onhand_tag_id
INNER JOIN mfinv.dbo.FM_IC_PS_VARIETY V ON V.VARIETYIDX = d.VARIETYIDX
LEFT JOIN mfinv.dbo.FM_IC_TAG TG ON TG.TAGIDX = t.TAGIDX
WHERE h.onhand_id = (SELECT onhand_id FROM mfinv.dbo.onhand
WHERE onhand_id = IDENT_CURRENT (''mfinv.dbo.onhand''))
AND TG.ID IN ('+#TAG+')'
PRINT #SQL
EXEC (#SQL)
Here's what I did. Thank you all for responding. Thanks to dasblinkenlight for answering "How to replace first and last character of column in sql server?". Thanks to SQLMenace for answering "How Do I Split a Delimited String in SQL Server Without Creating a Function?".
Here's how I removed parenthesis:
#Tag nvarchar(256)
SET #Tag = SUBSTRING(#Tag, 2, LEN(#Tag)-2)
Here's how I split and rebuilt #Tag:
AND TG.ID in
(
SELECT SUBSTRING(',' + #Tag + ',', Number + 1,
CHARINDEX(',', ',' + #Tag + ',', Number + 1) - Number -1)AS VALUE
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + #Tag + ',') - 1
AND SUBSTRING(',' + #Tag + ',', Number, 1) = ','
)

Update 13 million rows - SQL Server 2008

How can I update 13 million rows in stages by using a cursor or something?
Updating with the current script runs for days and still haven't finished.
There is a row_id field. 1 - 13m
Only one field needs to be updated.
UPDATE
[CIPC].[dbo].[tbldirector]
SET
[CIPC].[dbo].[tbldirector].ENT_NUM = REG.Ent_Number
FROM
[CIPC].[dbo].[tbldirector] DIR
INNER JOIN
[Cipc].[dbo].[tblregister] REG
ON
DIR.ENT_LONGNAME = REG.ENT_NAME
in this case you don't need cursor. You can do it with a loop like this.
DECLARE #indx int, #StepSize INT
SET #indx = 1
SET #StepSize = 100000
BEGIN TRAN
WHILE (EXISTS(SELECT 0 FROM [CIPC].[dbo].[tbldirector] WHERE row_id >= #indx))
BEGIN
PRINT 'Going to update indx ' + REPLICATE(CONVERT(VARCHAR, #indx) + ' -- ' + CONVERT(VARCHAR, #indx + #StepSize) + ' | ', 200)
UPDATE [CIPC].[dbo].[tbldirector]
SET [CIPC].[dbo].[tbldirector].ENT_NUM = REG.Ent_Number
FROM [CIPC].[dbo].[tbldirector] DIR
INNER JOIN [Cipc].[dbo].[tblregister] REG
ON DIR.ENT_LONGNAME = REG.ENT_NAME
WHERE row_id BETWEEN #indx AND #indx + #StepSize
SELECT #indx = #indx + #StepSize
SELECT REPLICATE(LEFT(CONVERT(VARCHAR, #indx) + ' | ', 10), 200)
END
COMMIT
This below link have three methods to do the needed,
http://ksadba.wordpress.com/2008/06/16/updating-millions-of-rows-merge-vs-bulk-collect/

Resources