delete in small chunks from cursor data - T-SQL stored Procedure - sql-server

i have written this t-sql stored procedure.
This is working fine , but since this stored procedure will be used to delete a lot of data (for example 1M to 2M) , I think, this can cause some locks in the table, or cause some db performance etc. So I am thinking, if we delete in batch for example at a time delete 1000 records. I am not totally sure about how to do this without cause any issue in db.
ALTER PROCEDURE [schema].[purge_data] #count INT---(count input can be in millions)
AS
DECLARE #p_number VARCHAR(22)
,#p_r_number VARCHAR(5)
DECLARE data_cursor CURSOR
FOR
SELECT TOP (#count) JRC_policy_number
,jrc_part_range_nbr
FROM [staging].[test].[p_location]
WHERE JRC_POLICY_TERM_DT < CAST('19950101 00:00:00.000' AS DATETIME)
AND jrc_policy_status = 'T'
OPEN data_cursor
FETCH NEXT
FROM data_cursor
INTO #p_number
,#p_r_number
WHILE ##FETCH_STATUS = 0
BEGIN
DELETE
FROM [staging].[test].[p_location]
WHERE JRC_policy_number = #p_number
AND jrc_part_range_nbr = #p_r_number
FETCH NEXT
FROM data_cursor
INTO #p_number
,#p_r_number
END
CLOSE data_cursor
DEALLOCATE data_cursor
Edit :
I had already tried without cursor - direct delete query like below.
DELETE TOP (1000) FROM [MyTab] WHERE YourConditions
It was very fast , it took 34 seconds to delete 1M records, but , during the 34 seconds, the table was locked completely. In production p_locator table is being used 24/7 , and being used by a very critical application, which expects response time in milliseconds, our purge script should not impact the the main application in any way. that's why I have chosen this cursor approach. pls guide

With some of your references I've written the below stored proc. Ofcourse there will be ALOT of scope for improvements. Pls share.
ALTER PROCEDURE [dbo].[purge_data] #count INT
AS
DECLARE #iteration INT
,#remainder INT
,#current_count INT
BEGIN
SELECT #current_count = count(*)
FROM PROD_TBL
WHERE JRC_POLICY_TERM_DT < CAST('19950101 00:00:00.000' AS DATETIME)
AND JRC_POLICY_STATUS = 'T'
AND JRC_PLCY_ADMIN_SYS_CD = 'X'
IF (#current_count < #count)
BEGIN
SET #count = #current_count
END
SET #iteration = #count / 10000
SET #remainder = #count % 10000
WHILE (#iteration > 0)
BEGIN
DELETE
FROM PROD_TBL
FROM (
SELECT TOP 10000 JRC_POLICY_NUMBER
,JRC_PART_RANGE_NBR
,JRC_PLCY_ADMIN_SYS_CD
FROM PROD_TBL
WHERE JRC_POLICY_TERM_DT < CAST('19950101 00:00:00.000' AS DATETIME)
AND JRC_POLICY_STATUS = 'T'
AND JRC_PLCY_ADMIN_SYS_CD = 'X'
) pol_locator_tbl
WHERE PROD_TBL.JRC_POLICY_NUMBER = pol_locator_tbl.JRC_POLICY_NUMBER
AND PROD_TBL.JRC_PART_RANGE_NBR = pol_locator_tbl.JRC_PART_RANGE_NBR
AND PROD_TBL.JRC_PLCY_ADMIN_SYS_CD=pol_locator_tbl.JRC_PLCY_ADMIN_SYS_CD
SET #iteration = #iteration - 1
END
IF (#remainder > 0)
BEGIN
DELETE
FROM PROD_TBL
FROM (
SELECT TOP (#remainder) JRC_POLICY_NUMBER
,JRC_PART_RANGE_NBR
,JRC_PLCY_ADMIN_SYS_CD
FROM PROD_TBL
WHERE JRC_POLICY_TERM_DT < CAST('19950101 00:00:00.000' AS DATETIME)
AND JRC_POLICY_STATUS = 'T'
AND JRC_PLCY_ADMIN_SYS_CD = 'X'
) pol_locator_tbl
WHERE PROD_TBL.JRC_POLICY_NUMBER = pol_locator_tbl.JRC_POLICY_NUMBER
AND PROD_TBL.JRC_PART_RANGE_NBR = pol_locator_tbl.JRC_PART_RANGE_NBR
AND PROD_TBL.JRC_PLCY_ADMIN_SYS_CD=pol_locator_tbl.JRC_PLCY_ADMIN_SYS_CD
END
END
END

Related

Time slots adding through trigger in SQL Server

I am trying to have the trigger to add time slots in timeslots table each time the doctor insert a record of his availability in the availability table.
I wrote the trigger, it saved. but when I insert a record into availability, the trigger seems not triggered, yet I do not have any error message.
Can somebody help me? Badly needed.
Here is the code for trigger
ALTER TRIGGER [dbo].[trigger_addSlots]
ON [dbo].[availability]
FOR INSERT AS
BEGIN
DECLARE #AvailabilityId INT
DECLARE #SlotStart DATETIME
DECLARE #SlotEnd DATETIME
DECLARE #NumberOfSlots INT
DECLARE #Duration INT
DECLARE #SlotDoctorId INT
DECLARE #i INT
SELECT #AvailabilityId = Id FROM inserted
SELECT #SlotDoctorId = DoctorId FROM inserted
SELECT #SlotStart = AvailableFrom FROM inserted
SELECT #SlotEnd = AvailableTo FROM inserted
SELECT #Duration = AppointmentDuration FROM inserted
SET #NumberOfSlots = CONVERT(INT, (#SlotEnd - #SlotStart)) / #Duration
SET #i = 0;
WHILE #i < #NumberOfSlots
BEGIN
INSERT INTO timeslots(AvailabilityId, SlotStart, SlotEnd, SlotDoctorId, IsAvailable)
VALUES (#AvailabilityId, #SlotStart, #SlotEnd, #SlotDoctorId, 1)
SET #SlotStart = #SlotEnd
SET #i = #i + 1
END
END

SQL Server if statement does not execute as expected

I am trying to use the following stored procedure but there are some instances WHERE only the incremental happens AND the code does not run. What I need is that, when the program enters the IF statement, either it should run both the statements or None.
Stored procedure goes like this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spflpunch]
AS
BEGIN
DECLARE #id NUMERIC(18,0)
DECLARE #studname NVARCHAR(50)
DECLARE #punchtime DATETIME
DECLARE #samedaycount NUMERIC(2)
SELECT #id = (MAX(lastid)) FROM [smartswype].[dbo].[read]
PRINT #id
SELECT #studname = studname
FROM [SSWYPE_WEBDB].[dbo].[attdview]
WHERE id =#id
PRINT #studname
SELECT #punchtime = punchtime
FROM [SSWYPE_WEBDB].[dbo].[attdview]
WHERE id = #id
PRINT #punchtime
--SELECT #punchvarchar = CONVERT(VARCHAR(10),#punchtime, 103) + ' ' + CONVERT(VARCHAR(5), #punchtime, 14)
IF #id = (SELECT MAX(id) FROM [SSWYPE_WEBDB].[dbo].[attdview])
BEGIN
SELECT #samedaycount = COUNT(*)
FROM [SSWYPE_WEBDB].[dbo].[attdview]
WHERE (studname = #studname
AND CONVERT(DATE, punchtime) = CONVERT(DATE, #punchtime)) -- If firstpunch = 1 then it is the first punch
PRINT #samedaycount
IF #samedaycount =1
BEGIN
INSERT INTO [smartswype].[dbo].[firstlastpunch] ([studname], [DATE], [punch1], [punch2])
VALUES(#studname, CONVERT(DATE, #punchtime), #punchtime, NULL);
UPDATE [smartswype].[dbo].[read]
SET lastid = #id + 1;
END
ELSE IF (#samedaycount > 1)
BEGIN
UPDATE [smartswype].[dbo].[firstlastpunch]
SET punch2 = #punchtime
WHERE (studname = #studname AND DATE = CONVERT(DATE, #punchtime));
UPDATE [smartswype].[dbo].[read]
SET lastid = #id + 1;
END
END
END
If you want to ensure that both or none of the statements run, you should wrap the contents of the if statement in a transaction.
By wrapping it in a transaction, you can ensure that if one statement fails, that the other statement will not run.
Here is a link to the docs on transactions in SQL Server
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/commit-transaction-transact-sql

SQL Procedure Meaning

Can someone to explain me what the next procedure does?
CREATE PROCEDURE [add_100*Clients-runView2-del_50*Reductions] AS
DECLARE #procName NVARCHAR(100) = OBJECT_NAME(##PROCID), #currentName NVARCHAR(50)
DECLARE #index int
INSERT INTO TestRuns (Description, StartAt, EndAt)
VALUES ('Add Clients - View 2 - Delete Reductions', GETDATE(), null)
DECLARE #currentID int
SET #currentID = (SELECT SCOPE_IDENTITY())
SET #index = CHARINDEX('-', #procName)
WHILE #index > 0
BEGIN
SET #currentName = SUBSTRING(#procName, 1, #index-1)
SET #procName = SUBSTRING(#procName, #index+1, (LEN(#procName) - #index))
SET #index = CHARINDEX('-', #procName)
EXEC #currentName
END
SET #currentName = #procName
EXEC #currentName
UPDATE TestRuns
SET EndAt = GETDATE()
WHERE TestRunID = #currentID
GO
I can't understand what does getDate and how it influences the tables.
The purpose of the procedure lies within
EXEC #currentName.
I believe this is some sort of performance test where you see how much time procedures takes to run
I guess you are passing some sort of procedure names separated by - and parse each procedure and run them.
While running them , you are recording your start of the time using GetDate and after everything is run, end of the run using GetDate.(As GetDate gives you the current time, the difference will tell you how long did it take to run all the procedures.
You record that information in an audit table called TestRuns.

Insert trigger for duplicate records not working sql server

ALTER TRIGGER [dbo].[STOK_HARKETLERI_Insert]
ON [dbo].[STOK_HAREKETLERI]
FOR INSERT
AS BEGIN
declare #tip int
declare #miktar float
declare #stokkod nvarchar
declare #tarih datetime
declare #counter int
Select
#tip = sth_tip, #miktar = sth_miktar,
#stokkod = sth_stok_kod, #tarih = sth_tarih
from inserted
select #Counter = COUNT(sth_tip)
from STOK_HAREKETLERI
where sth_evraktip = 6
and sth_tip = #tip
and sth_miktar = #miktar
and #stokkod = sth_stok_kod
and #tarih = sth_tarih
if (#counter>=1)
begin
rollback
RAISERROR ('Record already exists', 17, -1) with log
end
END
GO
The trigger is not being triggered on insert statements, however if I remove the variables and fill the data and run it on SQL Server it is running fine.
Any suggestions?
One more thing if I change the line (#counter >= 1) to (#counter >= 0) it starts working again.
If you insert more than one row, there will be more than one row in "inserted", but you're only checking the last of them. It might be easier to make a check constraint, depending on what the rules regarding "sth_evraktip = 6 " actually are (can there be more rows done with update later etc).
With insert trigger something like this might work:
if exists (select 1 from inserted i
where exists(select 1 from STOK_HAREKETLERI S
where S.sth_evraktip = 6
and S.sth_tip = i.sth_tip
and S.sth_miktar = i.sth_miktar
and S.sth_stok_kod = i.sth_stok_kod
and S.sth_tarih = i.sth_tarih
and S.sth_RECno < i.sth_RECno)) begin
rollback
RAISERROR ('Record already exists', 17, -1) with log
end
If any of the columns can contain NULL, then you'll have to add more logic to handle that.
If i skip the variable declaration and value passing to variable trigger works flawlessly.FOR EACH LINE in my case
Edited code is posted below.
Create TRIGGER [dbo].[STOK_HARKETLERI_Insert]
ON [dbo].[STOK_HAREKETLERI]
FOR INSERT
AS
BEGIN
Declare #counter int
select #counter = COUNT(sth_tip) from STOK_HAREKETLERI
where sth_evraktip = 6
and sth_tip = (select sth_tip from inserted i)
and sth_miktar =(select sth_miktar from inserted)
and sth_stok_kod =(select sth_stok_kod from inserted)
and sth_tarih = (select sth_tarih from inserted)
and sth_RECno < (select sth_RECno from inserted)
if (#counter>=1)
begin
rollback
RAISERROR ('Record already exists', 17, -1) with log
end
END

SQL Server inserting huge number of rows to a table with default values and identity column in it

I need to insert about 6400000 rows a table with 2 columns
CREATE TABLE [DBName].[DBO].[BigList]
(
[All_ID] [int] identity(1,1) NOT NULL,
[Is_It_Occupied] [int] default(0) not null
)
I am using the following code today, which takes very long time about 100 minutes.
SET #NumberOfRecordsToInsert = 6400000;
WHILE (#NumberOfRecordsToInsert > 0)
BEGIN
INSERT [DBName].[DBO].[BigList] DEFAULT VALUES;
SET #NumberOfRecordsToInsert = #NumberOfRecordsToInsert - 1
END
Does anyone have a better way to do this?
Grab a hold of 6400000 rows from somewhere and insert them all at once.
insert into BigList(Is_It_Occupied)
select top(6400000) 0
from sys.all_objects as o1
cross join sys.all_objects as o2
cross join sys.all_objects as o3
Did some testing on how long time the different solutions took on my computer.
Solution Seconds
-------------------------------------------------- -----------
Mikael Eriksson 13
Naresh 832
Dd2 25
TToni 92
Milica Medic 90
marc_s 2239
Your main problem is that each statement runs within a separate transaction. Putting everything in one transaction isn't advisable because very large transactions create their own problems.
But the biggest bottleneck in your code will be the I/O on the transaction log. The following code achieves a 14 MB/s overall write rate on my Laptop (with a Samsung 840 SSD) and runs in 75 seconds:
DECLARE #NumberOfRecordsToInsert INT = 6400000;
DECLARE #Inner INT = 10000;
SET NOCOUNT ON
WHILE (#NumberOfRecordsToInsert > 0)
BEGIN
BEGIN TRAN
SET #Inner = 0
WHILE (#Inner < 10000)
BEGIN
INSERT [BigList] DEFAULT VALUES;
SET #Inner = #Inner+1
END
COMMIT
SET #NumberOfRecordsToInsert = #NumberOfRecordsToInsert - #Inner
END
Why don't you use this:
INSERT [DBName].[DBO].[BigList] DEFAULT VALUES;
GO 6400000
in SQL Server Management STudio, this will execute the command as many times as you specify in the GO xxxx statement
But even so: inserting over 6 million rows will take some time!
You can try something like this, it took me less time than running your query:
SET NOCOUNT ON
BEGIN TRAN
DECLARE #i INT
SET #i = 1
WHILE #i <= 6400000
BEGIN
INSERT INTO [DBName].[DBO].[BigList] DEFAULT VALUES
SET #i = #i + 1
END
COMMIT TRAN
Hope it helps
DECLARE #NoRows INT
DECLARE #temp AS TABLE (Is_It_Occupied INT)
SET #NoRows = 1000
WHILE (#NoRows > 0)
BEGIN
INSERT INTO #temp (Is_It_Occupied) VALUES (0)
SET #NoRows = #NoRows - 1
END
SET #NoRows = 6400
WHILE (#NoRows > 0)
BEGIN
INSERT INTO BigList (Is_It_Occupied)
SELECT Is_It_Occupied FROM #temp
SET #NoRows = #NoRows - 1
END

Resources