Update SQL rows one row at a time - triggers

I am trying to do a mass update on our SQL server which would easily be done by the following code in our situation.
update patient set security_level = '2'
where security_level = '1'
The problem is our SQL server connects to a mirth server that sends transactions to the State and the mirth server locks up if more than one row is updated at a time, so I'm hoping there is a way to update the rows one at a time. To prevent that our vendor who makes the software has several triggers in place. A piece of the code for the trigger is
IF (#numrows > 1)
BEGIN
IF ##TRANCOUNT > 0 ROLLBACK TRANSACTION
SELECT #errmsg = OBJECT_NAME(##PROCID) + ' : more than one row is updated in table Patient'
RAISERROR(#errmsg,16,21)
RETURN
END
If I were to disable the 3 triggers it would break other things. Thank you for any suggestions or thoughts you have.

declare #tmp_table table(
PRG int identity(1,1) Primary Key,
patient_id int
)
declare #start_value int = 1,
#finish_value int,
#patient_id int
Insert Into #tmp_table(patient_id ) Select patient_id From patient where security_level = '1'
Select #finish_value = max(PRG) From #tmp_table
While #start_value <= #finish_value
Begin
--now get a key for patient and store in variables
Select #patient_id = patient_id
From #tmp_table
Where PRG = #start_value
--.. and now update by key
Update patient
set security_level = '2'
Where patient_id = #patient_id
Set #start_value = #start_value + 1
End

Related

SQL job to ALTER VIEW

On a weekly base, data is updated from a source into one of the two identical tables: Table_ODD or Table_EVEN. A view transfers data from the active table to a reporting tool. Which table is currently active is stored in a table.
So the view is either like this:
Create View [dbo].[V_ReportData] AS SELECT * FROM [Table_ODD] -- for odd-week
or
Create View [dbo].[V_ReportData] AS SELECT * FROM [Table_EVEN] -- for even-week
The switch may only be performed after approval of the updated data, and will be done by an administrator
who will manually start the job.
I had like to create a job that will do the switch. I have tried this code:`
Declare #NumberOfTables as int
,#ActiveTable as nvarchar(100);
Set #NumberOfTables = (Select Count(*) from [Table_Status] where Upper([Status]) = 'ACTIVE' ) ;
if #NumberOfTables = 1
BEGIN
Set #ActiveTable = (Select UPPER([TABLE_NAME]) from [Table_Status] where Upper([Status]) = 'ACTIVE' );
END;
if #ActiveTable = 'TABLE_EVEN'
BEGIN
ALTER View [dbo].[V_TABLE_] AS
SELECT *
FROM [dbo].[TABLE_ODD]
UPDATE [Table_Status]
Set [STATUS] = 'ACTIVE'
Where [TABLE_NAME] = 'TABLE_ODD'
UPDATE [Table_Status]
Set [STATUS] = 'PASSIVE'
Where [TABLE_NAME] = 'TABLE_EVEN'
END
if #ActiveTable = 'TABLE_ODD'
BEGIN
ALTER View [dbo].[V_TABLE_] AS
SELECT *
FROM [dbo].[TABLE_EVEN]
UPDATE [Table_Status]
Set [STATUS] = 'ACTIVE'
Where [TABLE_NAME] = 'TABLE_EVEN'
UPDATE [Table_Status]
Set [STATUS] = 'PASSIVE'
Where [TABLE_NAME] = 'TABLE_ODD'
END
`
But The ALTER VIEW statement may not be included in any part of the coding.
So I think the ALTER VIEW statement should be in a separate step.
But that step must be activated conditionaly, namely based on the condition which table is active.
In short something like this:
if #ActiveTable = 'TABLE_EVEN'
then step 2
else step 3
DROP and CREATE view is not recommended because of authorizations that are granted to the view. With a DROP VIEW statement, all authorizations are drop as well.
How can I achieve this?
I took me a little while , but found the answer!
I had to create multiple steps first, before I could switch to the specific job step based on an error-code.
List of job steps
step 1: check if switch is valid, e.g. only one table is Active and DateTimeStamp of passive table (which should be the last updated) is more recent then active table
Declare #NumberOfTables as int
,#ActiveTable as nvarchar(100)
,#Update_DateTime_Active as datetime
,#Update_DateTime_Passive as datetime;
Set #NumberOfTables = (Select Count(*) from [Table_Status] where Upper([Status]) = 'ACTIVE' ) ;
if #NumberOfTables = 1
BEGIN
set #Update_DateTime_Active = (Select [UPDATED] from [Table_Status] where Upper([Status]) = 'ACTIVE' );
set #Update_DateTime_Passive = (Select [UPDATED] from [Table_Status] where Upper([Status]) <> 'ACTIVE' );
END
IF #Update_DateTime_Active > #Update_DateTime_Passive
RAISERROR ('Status Active table not compatible with Update DateTimestamp',16,2)
if #NumberOfTables <> 1
RAISERROR ('Tables Data_ODD and Data_EVEN have same status',16,3)
Step 2: determin which table is currently active
Declare #NumberOfTables as int
,#ActiveTable as nvarchar(100);
Set #NumberOfTables = (Select Count(*) from [Table_Status] where Upper([Status]) = 'ACTIVE' ) ;
if #NumberOfTables = 1
BEGIN
Set #ActiveTable = (Select UPPER([TABLE_NAME]) from [Data_Table_Status] where Upper([Status]) = 'ACTIVE' );
END
if #ActiveTable = 'DATA_EVEN'
BEGIN
RAISERROR('Now switch VIEW to table ...ODD',1,1)
END
This step determin the next step to be taken in the advanced settings of the step:
On succes action: goto step 3
on Error action: goto step 5
Remember: when ..EVEN is active, an error is raised, causing the job to go to step 5 (which is activating ..ODD)
Step 3: alter view to EVEN
ALTER View [dbo].[V_Data_] AS SELECT * FROM [dbo].[Data_EVEN]
step 4: updated status table (change status of ODD to Active, and EVEN to Passive)
UPDATE [Data_Table_Status]
Set [STATUS] = 'ACTIVE'
Where [TABLE_NAME] = 'Data_EVEN'
UPDATE [Data_Table_Status]
Set [STATUS] = 'PASSIVE'
Where [TABLE_NAME] = 'Data_ODD'
Step 5: alter view to ODD
step 6: updated status table (change status of ODD to Active, and EVEN to Passive)

DELETE WHERE NOT EXISTS Not Being Applied Correctly

I'm using NOT EXISTS during a DELETE statement in a stored procedure and the not exists is not being applied to the data.
Using the following example data:
CREATE TABLE Region
(
RegionID INT IDENTITY(1,1)
,RegionName VARCHAR(25)
)
GO
INSERT INTO Region(RegionName)
VALUES ('East Coast')
,('Mid West')
,('West Coast')
GO
CREATE TABLE Customer
(
CustomerID INT IDENTITY(1,1)
,FirstName VARCHAR(5)
,Region INT
)
GO
INSERT INTO Customer(FirstName,Region)
VALUES('Tom',1)
,('Mike',2)
,('Jean',3)
GO
CREATE TABLE Orders
(
OrderID INT IDENTITY(1,1)
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
GO
INSERT INTO Orders(CustomerID,OrderAmount,OrderDate)
VALUES(1,10,'2018-11-30')
,(2,12,'2018-11-30')
,(2,15,'2018-12-01')
,(2,8,'2018-12-02')
,(2,11,'2018-12-03')
,(3,13,'2018-12-01')
,(3,20,'2018-12-03')
GO
Using that data I'm trying to create a procedure that does the following:
CREATE PROCEDURE udsp_GetOrdersOfXAmount #OrderAmount INT, #RegionID INT = 0
AS
BEGIN
DECLARE #ProcedureTemp TABLE
(
OrderID INT
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
INSERT INTO #ProcedureTemp(OrderID,CustomerID,OrderAmount,OrderDate)
SELECT * FROM Orders WHERE OrderAmount >= #OrderAmount
--Do several other UPDATES/ DELETES to #ProcedureTemp
--This is where the issue lies
IF #RegionID > 0
BEGIN
DELETE T FROM #ProcedureTemp T
WHERE NOT EXISTS
(
SELECT *
FROM Customer C
JOIN #ProcedureTemp T ON T.CustomerID = C.CustomerID
WHERE C.Region = #RegionID
)
END
SELECT * FROM #ProcedureTemp
END
GO
If you execute the procedure with the #RegionID parameter populated, you will see the procedure is not honoring the filter by region.
E.G.
EXEC udsp_GetOrdersOfXAmount 10,3
However, if you run the sub query used in the DELETE statement as its own query, you will see the WHERE clause logic provided is working. I don't understand why the it isn't working when used with NOT EXISTS in the DELETE statement.
DECLARE #OrderAmount INT = 10, #RegionID INT = 3
DECLARE #ProcedureTemp TABLE
(
OrderID INT
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
INSERT INTO #ProcedureTemp(OrderID,CustomerID,OrderAmount,OrderDate)
SELECT * FROM Orders WHERE OrderAmount >= #OrderAmount
SELECT *
FROM Customer C
JOIN #ProcedureTemp T ON T.CustomerID = C.CustomerID
WHERE C.Region = #RegionID
Thank you in advance for any help you can provide.
You don't need the join in the inner query.
The fact that you are using the same alias for the outer query and the inner one is confusing to me, I'm guessing SQL Server also should have a problem with it.
Try writing it like this:
DELETE T
FROM #ProcedureTemp T
WHERE NOT EXISTS
(
SELECT *
FROM Customer C
-- You already have the T from the outer statement
WHERE T.CustomerID = C.CustomerID
AND C.Region = #RegionID
)

How to fill column basing on two other columns

I have table LessonHour with empty Number column.
TABLE [dbo].[LessonHour]
(
[Id] [uniqueidentifier] NOT NULL,
[StartTime] [time](7) NOT NULL,
[EndTime] [time](7) NOT NULL,
[SchoolId] [uniqueidentifier] NOT NULL,
[Number] [int] NULL
)
How can I fill up the table with Number for each LessonHour so it would be the number of lesson hour in order?
The LessonHours cannot cross each other. Every school has defined its own lesson hour schema.
Example set of data
http://pastebin.com/efWCtUbv
What'd I do:
Order by SchoolId and StartTime
Use Cursor to insert into row next number, starting from 1 every time the SchoolId changes.
Edit:
Solution with cursor
select -- top 20
LH.[Id],
[StartTime],
[EndTime],
[SchoolId]
into #LH
from
LessonHour as LH
join RowStatus as RS on LH.RowStatusId = RS.Id
where
RS.IsActive = 1
select * from #LH order by SchoolId, StartTime
declare #id uniqueidentifier, #st time(7), #et time(7), #sid uniqueidentifier
declare #prev_sid uniqueidentifier = NEWID()
declare #i int = 1
declare cur scroll cursor for
select * from #LH order by SchoolId, StartTime
open cur;
fetch next from cur into #id, #st, #et, #sid
while ##FETCH_STATUS = 0
begin
--print #prev_sid
if #sid <> #prev_sid
begin
set #i = 1
end
update LessonHour set Number = #i where Id = #id
print #i
set #i = #i + 1
set #prev_sid = #sid
fetch next from cur into #id, #st, #et, #sid
end;
close cur;
deallocate cur;
drop table #LH
This is the result I was after http://pastebin.com/iZ8cnA6w
Merging the information from the StackOverflow questions SQL Update with row_number() and
How do I use ROW_NUMBER()?:
with cte as (
select number, ROW_NUMBER() OVER(partition by schoolid order by starttime asc) as r from lessonhour
)
update cte
set number = r
Would this work
CREATE TABLE [dbo].[LessonHour]
(
[Id] [uniqueidentifier] NOT NULL,
[StartTime] [time](7) NOT NULL,
[EndTime] [time](7) NOT NULL,
[SchoolId] [uniqueidentifier] NOT NULL,
[Number] AS DATEDIFF(hour,[StartTime],[EndTime])
)
So if I understand the question correctly you require a calculated column which takes in the values of [StartTime] and [EndTime] and returns the number of hours for that lesson as an int. The above table definition should do the trick.

tsql trigger behaves abnormally

I have a trigger which is as follows:
ALTER TRIGGER [trigger_CATEGORY_VALUE_ID] ON [dbo].[tblA]
FOR UPDATE
AS
SET NOCOUNT ON
IF ( UPDATE([CATEGORY_VALUE_ID]))
BEGIN
INSERT INTO [dbo].[htblB]
( ID
, CATEGORY_VALUE_ID
, STATUS_END_DATE
, STATUS_END_DATE_SOURCE)
SELECT
t.ID
, t.CATEGORY_VALUE_ID
, GETDATE()
, t.UPDATE_SOURCE
FROM [dbo].[tblCAPITATION] t
INNER JOIN inserted ins
ON t.CATEGORY_VALUE_ID = ins.CATEGORY_VALUE_ID
END
What it needs to do is insert a new row in htblB when the column CATEGORY_VALUE_ID is updated. It works fine if only one row is updated. But if it has multiple row updates, then 2 to the power number of rows updated amount of new rows are inserted in htblB.
UPDATE dbo.tblCAPITATION
SET CAPITATION_STATUS_CATEGORY_VALUE_ID = '80574', UPDATE_SOURCE = 'TEST3'
WHERE CAPITATION_ID = 2 OR CAPITATION_ID = 3
This statement will insert 4 new rows to htblB instead of 2.
May you please shed some light on why this is hapening and how to prevent it?
Thanks!
I'm going to assume that ID is the primary key, if so then you should be joining inserted on ID not category_value_id
ALTER TRIGGER [trigger_CATEGORY_VALUE_ID] ON [dbo].[tblA]
FOR UPDATE
AS
SET NOCOUNT ON
IF ( UPDATE([CATEGORY_VALUE_ID]))
BEGIN
INSERT INTO [dbo].[htblB]
( ID
, CATEGORY_VALUE_ID
, STATUS_END_DATE
, STATUS_END_DATE_SOURCE)
SELECT
t.ID
, t.CATEGORY_VALUE_ID
, GETDATE()
, t.UPDATE_SOURCE
FROM [dbo].[tblCAPITATION] t
INNER JOIN inserted ins
ON t.ID = ins.ID
END

sql missing rows when grouped by DAY, MONTH, YEAR

If I select from a table group by the month, day, year,
it only returns rows with records and leaves out combinations without any records, making it appear at a glance that every day or month has activity, you have to look at the date column actively for gaps. How can I get a row for every day/month/year, even when no data is present, in T-SQL?
Create a calendar table and outer join on that table
My developer got back to me with this code, underscores converted to dashes because StackOverflow was mangling underscores -- no numbers table required. Our example is complicated a bit by a join to another table, but maybe the code example will help someone someday.
declare #career-fair-id int
select #career-fair-id = 125
create table #data ([date] datetime null, [cumulative] int null)
declare #event-date datetime, #current-process-date datetime, #day-count int
select #event-date = (select careerfairdate from tbl-career-fair where careerfairid = #career-fair-id)
select #current-process-date = dateadd(day, -90, #event-date)
while #event-date <> #current-process-date
begin
select #current-process-date = dateadd(day, 1, #current-process-date)
select #day-count = (select count(*) from tbl-career-fair-junction where attendanceregister <= #current-process-date and careerfairid = #career-fair-id)
if #current-process-date <= getdate()
insert into #data ([date], [cumulative]) values(#current-process-date, #day-count)
end
select * from #data
drop table #data
Look into using a numbers table. While it can be hackish, it's the best method I've come by to quickly query missing data, or show all dates, or anything where you want to examine values within a range, regardless of whether all values in that range are used.
Building on what SQLMenace said, you can use a CROSS JOIN to quickly populate the table or efficiently create it in memory.
http://www.sitepoint.com/forums/showthread.php?t=562806
The task calls for a complete set of dates to be left-joined onto your data, such as
DECLARE #StartInt int
DECLARE #Increment int
DECLARE #Iterations int
SET #StartInt = 0
SET #Increment = 1
SET #Iterations = 365
SELECT
tCompleteDateSet.[Date]
,AggregatedMeasure = SUM(ISNULL(t.Data, 0))
FROM
(
SELECT
[Date] = dateadd(dd,GeneratedInt, #StartDate)
FROM
[dbo].[tvfUtilGenerateIntegerList] (
#StartInt,
,#Increment,
,#Iterations
)
) tCompleteDateSet
LEFT JOIN tblData t
ON (t.[Date] = tCompleteDateSet.[Date])
GROUP BY
tCompleteDateSet.[Date]
where the table-valued function tvfUtilGenerateIntegerList is defined as
-- Example Inputs
-- DECLARE #StartInt int
-- DECLARE #Increment int
-- DECLARE #Iterations int
-- SET #StartInt = 56200
-- SET #Increment = 1
-- SET #Iterations = 400
-- DECLARE #tblResults TABLE
-- (
-- IterationId int identity(1,1),
-- GeneratedInt int
-- )
-- =============================================
-- Author: 6eorge Jetson
-- Create date: 11/22/3333
-- Description: Generates and returns the desired list of integers as a table
-- =============================================
CREATE FUNCTION [dbo].[tvfUtilGenerateIntegerList]
(
#StartInt int,
#Increment int,
#Iterations int
)
RETURNS
#tblResults TABLE
(
IterationId int identity(1,1),
GeneratedInt int
)
AS
BEGIN
DECLARE #counter int
SET #counter= 0
WHILE (#counter < #Iterations)
BEGIN
INSERT #tblResults(GeneratedInt) VALUES(#StartInt + #counter*#Increment)
SET #counter = #counter + 1
END
RETURN
END
--Debug
--SELECT * FROM #tblResults