update table using cursor? - sql

Update
the purpose of this excerise is to eliminate passing the #RegModifiedDateTime again what i want is i should be able to read ModifiedDateTime by passing Id
example: if i pass Id = 564 then i should be able to read
`schoold_Id and ModifiedDateTime`
end update
here is how my table looks like for SchoolRegistration:
school_id id active modifydatetime
--------------------------------------------------
432 564 1 2008-12-14 13:15:38.750
342 564 1 2008-12-14 14:15:50.470
353 564 1 2008-12-14 14:19:46.703
end update
how do i loop to update my SchoolRegistration table? the id might have 1 or many rows in the SchoolRegistration but the thing is that RegModifiedDateTime is a unique for concurrency purpose and i should loop to get the right modifydatetime for that id.
alter procedure [dbo].[del_schoolRegistration]
#Id bigint,
#RegModifiedDateTime datetime
as
begin
declare #rowsAffected int
begin tran
--registration
update SchoolRegistration
set Active = 0,
ModifiedDateTime = getdate()
where (Id = #Id and RegModifiedDateTime = #RegModifiedDateTime or #RegModifiedDateTime is null )
if (#rowsAffected < 1) begin
rollback tran
end
else begin
commit tran
end
return #rowsAffected
end

--registration
;with tmp as (
select *, rn=ROW_NUMBER() over (partition by ID order by RegModifiedDateTime desc)
from SchoolRegistration
where (Id = #Id and RegModifiedDateTime = #RegModifiedDateTime or #RegModifiedDateTime is null ))
update tmp
set Active = 0,
ModifiedDateTime = getdate()
WHERE rn=1
What happens here is that if you did not know the RegModifiedDateTime you are looking for (by passing #RegModifiedDateTime as NULL), the query will catch them all for the ID due to #RegModifiedDateTime is null, but update ONLY the LATEST RegModifiedDateTime based on the row_numbering and CTE table definition.
EDIT
The above query retains the option to pass in a direct #RegModifiedDateTime should a record other than the latest need updating. To always update only the latest, drop the WHERE filter against #RegModifiedDateTime completely
--registration
;with tmp as (
select *, rn=ROW_NUMBER() over (partition by ID order by RegModifiedDateTime desc)
from SchoolRegistration
where Id = #Id)
update tmp
set Active = 0,
ModifiedDateTime = getdate()
WHERE rn=1

i endup using curor:
USE AdventureWorks
GO
DECLARE #ProductID INT
DECLARE #getProductID CURSOR
SET #getProductID = CURSOR FOR
SELECT ProductID
FROM Production.Product
OPEN #getProductID
FETCH NEXT
FROM #getProductID INTO #ProductID
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #ProductID
FETCH NEXT
FROM #getProductID INTO #ProductID
END
CLOSE #getProductID
DEALLOCATE #getProductID
GO

Related

Count the amount of Cases and set Status on CaseStatus table if every counted amount is the statusCode 601 in the Cursor

Hello I made a cursor that check every row and check the status of the Caseinventory and then sets the status on the caseStatus. Problem is if I set the last counted row to 601 and it changes the code to 3 (shown in the code) it will not check if the rest is 601 and then just set the status to 3 even though rest is 600 or 599.
How can I solve this? should I count the amount on the CaseInventory and then check the status on every counted row and then update if the value is 601? and how do you do this? first time using cursor.
ALTER PROCEDURE [dbo].[SetsStatusOnCaseStatusByCaseInventoryStatus]
#StatusID INT,
#ID int,
#CaseInventoryID int
AS
BEGIN
--find caseID on CaseInventoryID
declare #CaseID int = (SELECT CaseID from [dbo].[factCaseInventory] where ID=#ID)
--CaseStatus
declare #CaseIkkeStartet int =1
declare #CaseIgangStatus int = 2
declare #CaseDoneStatus int = 3
--CaseInventoryStatus
DECLARE #NotStarted int = 599
DECLARE #Nocode int = 600
declare #YesCode int = 601
--Cursor
declare CaseInventoryCursor cursor for
Select ID, StatusID, CaseID from [dbo].[factCaseInventory]
open CaseInventoryCursor
fetch next from CaseInventoryCursor into #ID, #StatusID, #CaseID
while(##FETCH_STATUS = 0)
begin
SELECT #StatusID =StatusID from [dbo].[factCaseInventory] where CaseID=#CaseID
if(#StatusID = #Nocode)
begin
update [dbo].[factCase] set CaseStatusID=#CaseDoneStatus where CaseId=#CaseID
end
else if (#StatusID =#YesCode)
begin
update [dbo].[factCase] set CaseStatusID=#CaseIgangStatus where CaseId=#CaseID
end
else if (#StatusID =#NotStarted)
begin
update [dbo].[factCase] set CaseStatusID=#CaseIkkeStartet where CaseId=#CaseID
end
fetch next from CaseInventoryCursor into #ID, #StatusID, #CaseID
end
close CaseInventoryCursor
deallocate CaseInventoryCursor
END
It's unclear to me why you're passing parameters to your stored procedure when you then either don't use them, or overwrite them with new values before you use them.
However, if what you're trying to do is update each factCase record with a new CaseStatusID value based upon the related factCaseInventory record's StatusID value, you can just do a simple UPDATE with a join:
update fc set CaseStatusID =
case fci.StatusID
when 600 then 3
when 601 then 2
when 599 then 1
end
from factCaseInventory fci
left join factCase fc on fvi.CaseID = fc.CaseID

Generating dummy data from existing data set is slow using cursor

I'm trying to generate dummy data from the existing data I have in the tables. All I want is to increase the number of records in Table1 to N specified amount. The other tables should increase based on the foreign key references.
The tables has one to many relationship. For one record in table 1, I can have multiple entries in table 2, and in table 3 I can have many records based on IDs of the second table.
Since IDs are primary keys, I either capture it by
SET #NEWLY_INSERTED_ID = SCOPE_IDENTITY()
after inserting to table 1 and using in insert for table2, or inserting them to temp table and joining them to achieve the same results for table 3.
Here's the approach I'm taking with the CURSOR.
DECLARE #MyId as INT;
DECLARE #myCursor as CURSOR;
DECLARE #DESIRED_ROW_COUNT INT = 70000
DECLARE #ROWS_INSERTED INT = 0
DECLARE #CURRENT_ROW_COUNT INT = 0
DECLARE #NEWLY_INSERTED_ID INT
DECLARE #LANGUAGE_PAIR_IDS TABLE ( LangugePairId INT, NewId INT, SourceLanguage varchar(100), TargetLangauge varchar(100) )
WHILE (#ROWS_INSERTED < #DESIRED_ROW_COUNT)
BEGIN
SET #myCursor = CURSOR FOR
SELECT Id FROM MyTable
SET #CURRENT_ROW_COUNT = (SELECT COUNT(ID) FROM MyTable)
OPEN #myCursor;
FETCH NEXT FROM #myCursor INTO #MyId;
WHILE ##FETCH_STATUS = 0
BEGIN
IF ((#CURRENT_SUBMISSION_COUNT < #DESIRED_ROW_COUNT) AND (#ROWS_INSERTED < #DESIRED_ROW_COUNT))
BEGIN
INSERT INTO [dbo].[MyTable]
([Column1]
([Column2]
([Column3]
)
SELECT
,convert(numeric(9,0),rand() * 899999999) + 100000000
,COlumn2
,Colum3
FROM MyTable
WHERE Id = #MyId
SET #NEWLY_INSERTED_ID = SCOPE_IDENTITY()
INSERT INTO [dbo].[Language]
([MyTable1Id]
,[Target]
,[Source]
OUTPUT inserted.Id, inserted.MyTable1Id, inserted.Source, inserted.[Target] INTO #LANGUAGE_PAIR_IDS (LangugePairId, NewId, SourceLanguage, TargetLangauge)
SELECT
#NEWLY_INSERTED_ID
,[Target]
,[Source]
FROM [dbo].[Language]
WHERE MyTableId = #MyId
ORDER BY Id
DECLARE #tbl AS TABLE (newLanguageId INT, oldLanguageId INT, sourceLanguage VARCHAR(100), targetLanguage VARCHAR(100))
INSERT INTO #tbl (newLanguageId, oldLanguageId, sourceLanguage, targetLanguage)
SELECT 0, id, [Source], [Target] MyTable1Id FROM Language WHERE MyTable1Id = #MyId ORDER BY Id
UPDATE t
SET t.newlanguageid = lp.LangugePairId
FROM #tbl t
JOIN #LANGUAGE_PAIR_IDS lp
ON t.sourceLanguage = lp.SourceLanguage
AND t.targetLanguage = lp.TargetLangauge
INSERT INTO [dbo].[Manager]
([LanguagePairId]
,[UserId]
,[MyDate])
SELECT
tbl.newLanguageId
,p.[UserId]
,p.[MyDate]
FROM Manager m
INNER JOIN #tbl tbl
ON m.LanguagePairId = tbl.oldLanguageId
WHERE m.LanguagePairId in (SELECT Id FROM Language WHERE MyTable1Id = #MyId) -- returns the old language pair id
SET #ROWS_INSERTED += 1
SET #CURRENT_ROW_COUNT +=1
END
ELSE
BEGIN
PRINT 'REACHED EXIT'
SET #ROWS_INSERTED = #DESIRED_ROW_COUNT
BREAK
END
FETCH NEXT FROM #myCursor INTO #MyId;
END
CLOSE #myCursor
DEALLOCATE #myCursor
END
The above code works! It generates the data I need. However, it's very very slow. Just to give some comparison. Initial load of data for table 1 was ~60,000 records, Table2: ~74,000 and Tabl3 ~3,400
I tried to insert 9,000 rows in Table1. With the above code, it took 17:05:01 seconds to complete.
Any suggestion on how I can optimize the query to run little faster? My goal is to insert 1-2 mln records in Table1 without having to wait for days. I'm not tied to CURSOR. I'm ok to achieve the same result in any other way possible.

after trigger update statement take long time

I created a trigger as below
alter trigger sale_Trigger_Update on sale
after update
as
begin
Declare #old_value varchar(50)
Declare #new_value varchar(50)
Declare #sale_id UNIQUEIDENTIFIER
DECLARE new_cur CURSOR FORWARD_ONLY READ_ONLY LOCAL FOR
SELECT saleid
FROM INSERTED
open new_cur
Fetch Next from new_cur into #sale_id
while ##FETCH_STATUS = 0
Begin
set #old_value = (select enddate from deleted where SaleID = #sale_id)
set #new_value = (select enddate from inserted where SaleID = #sale_id)
insert into zzz (old_value,new_value) values(#old_value,#new_value)
end
CLOSE new_cur
DEALLOCATE new_cur
end
Then I do an update statement as below
update sale
set enddate = null
Sale table contain only 2 rows
and the execution is continuing unlimited.
I tried
update sale
set enddate = null
where saleid = 10
same problem.
Then I forcefully stopped the execution. Then checked the sale table and zzz table. No changes happened.
I am sure there is some issue in cursor. Can somebody show some light on it.
****Edited****
Actually I need to check enddate in deleted is null and enddate in inserted is not null
open new_cur
Fetch Next from new_cur into #sale_id
while ##FETCH_STATUS = 0
Begin
set #old_value = (select enddate from deleted where SaleID = #sale_id)
set #new_value = (select enddate from inserted where SaleID = #sale_id)
if #old_value = null and #new_value != null
begin
SELECT approval.*,
(select diag.*
from diag diag
where approval.id =diag.id
FOR XML PATH('diag'), TYPE
),
(select ser.*
from ser ser
where approval.id =ser.id
FOR XML PATH('ser'), TYPE
)
FROM approval approval,
where approval.id = 1
and approval.saleid =#saleid
FOR XML PATH, ELEMENTS,
root('Head')
end if
end
CLOSE new_cur
DEALLOCATE new_cur
Regarding the cursor.
Could you replace your trigger with?
alter trigger sale_Trigger_Update on sale
after update
as
begin
insert into zzz (old_value,new_value)
select
--i.SalesID,
d.enddate,
i.enddate
from inserted i
inner join deleted d on
i.SaleID = d.SaleID
where
d.enddate is null and
i.enddate is not null
end

Update table with new value for each row

I need to update a column (type of datetime) in the top 1000 rows my table. However the catch is with each additional row I must increment the GETDATE() by 1 second... something like DATEADD(ss,1,GETDATE())
The only way I know how to do this is something like this:
UPDATE tablename
SET columnname = CASE id
WHEN 1 THEN DATEADD(ss,1,GETDATE())
WHEN 2 THEN DATEADD(ss,2,GETDATE())
...
END
Obviously this is not plausible. Any ideas?
How about using id rather than a constant?
UPDATE tablename
SET columnname = DATEADD(second, id, GETDATE() )
WHERE id <= 1000;
If you want the first 1000 rows (by id), but the id has gaps or other problems, then you can use a CTE:
with toupdate as (
select t.*, row_number() over (order by id) as seqnum
from tablename
)
update toupdate
set columnname = dateadd(second, seqnum, getdate())
where seqnum <= 1000;
I don't know what your ID is like, and I'm assuming you have at least SQL Server 2008 or else ROW_NUMBER() won't work.
Note: I did top 2 to show you that you that the top works. You can change it to top 1000 for your actual query.
DECLARE #table TABLE (ID int, columnName DATETIME);
INSERT INTO #table(ID)
VALUES(1),(2),(3);
UPDATE #table
SET columnName = DATEADD(SECOND,B.row_num,GETDATE())
FROM #table A
INNER JOIN
(
SELECT TOP 2 *, ROW_NUMBER() OVER (ORDER BY ID) row_num
FROM #table
ORDER BY ID
) B
ON A.ID = B.ID
SELECT *
FROM #table
Results:
ID columnName
----------- -----------------------
1 2015-03-31 13:11:59.760
2 2015-03-31 13:12:00.760
3 NULL
You don't make explicit the SQL Server version you're using, so I will assume SQL Server 2005 or above. I believe the WAITFOR DELAY command would be a good option to keep adding 1 second to each rows of the datetime column.
See this example:
-- Create temp table
CREATE TABLE #Client
(
RecordID int identity(1,1),
[Name] nvarchar(100) not null,
PurchaseDate datetime null
)
-- Fill in temp table with example values
INSERT INTO #Client
VALUES ( 'Jhon', NULL)
INSERT INTO #Client
VALUES ( 'Martha', NULL)
INSERT INTO #Client
VALUES ( 'Jimmy', NULL)
-- Create local variables
DECLARE #currentRecordId int,
#currentName nvarchar(100)
-- Create cursor
DECLARE ClientsCursor CURSOR FOR
SELECT RecordID,
[Name]
FROM #Client
OPEN ClientsCursor
FETCH FROM ClientsCursor INTO #currentRecordId, #currentName
-- Check ##FETCH_STATUS to see if there are any more rows to fetch.
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE #Client
SET PurchaseDate = DATEADD(ss,1,GETDATE())
WHERE RecordID = #currentRecordId
AND [Name] = #currentName
WAITFOR DELAY '00:00:01.000'
FETCH NEXT FROM ClientsCursor INTO #currentRecordId, #currentName
END
CLOSE ClientsCursor;
DEALLOCATE ClientsCursor;
SELECT *
FROM #Client
And here's the result:
1 Jhon 2015-03-31 15:20:04.477
2 Martha 2015-03-31 15:20:05.473
3 Jimmy 2015-03-31 15:20:06.470
Hope you find this answer helpful
This should be what you need (at least a guidline)
DELIMITER $$
CREATE PROCEDURE ADDTIME()
BEGIN
DECLARE i INT Default 1 ;
simple_loop: LOOP
UPDATE tablename SET columnname = DATE_ADD(NOW(), INTERVAL i SECOND) where rownumber = i
SET i=i+1;
IF i=1001 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
END $$
To call that stored procedure use
CALL ADDTIME()

Loop through all the rows of a temp table and call a stored procedure for each row

I have declared a temp table to hold all the required values as follows:
DECLARE #temp TABLE
(
Password INT,
IdTran INT,
Kind VARCHAR(16)
)
INSERT INTO #temp
SELECT s.Password, s.IdTran, 'test'
from signal s inner join vefify v
on s.Password = v.Password
and s.IdTran = v.IdTran
and v.type = 'DEV'
where s.[Type] = 'start'
AND NOT EXISTS (SELECT * FROM signal s2
WHERE s.Password = s2.Password
and s.IdTran = s2.IdTran
and s2.[Type] = 'progress' )
INSERT INTO #temp
SELECT s.Password, s.IdTran, 'test'
FROM signal s inner join vefify v
on s.Password = v.Password
and s.IdTran = v.IdTran
and v.type = 'PROD'
where s.[Type] = 'progress'
AND NOT EXISTS (SELECT * FROM signal s2
WHERE s.Password = s2.Password
and s.IdTran = s2.IdTran
and s2.[Type] = 'finish' )
Now i need to loop through the rows in the #temp table and and for each row call a sp that takes all the parameters of #temp table as input.
How can I achieve this?
you could use a cursor:
DECLARE #id int
DECLARE #pass varchar(100)
DECLARE cur CURSOR FOR SELECT Id, Password FROM #temp
OPEN cur
FETCH NEXT FROM cur INTO #id, #pass
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC mysp #id, #pass ... -- call your sp here
FETCH NEXT FROM cur INTO #id, #pass
END
CLOSE cur
DEALLOCATE cur
Try returning the dataset from your stored procedure to your datatable in C# or VB.Net. Then the large amount of data in your datatable can be copied to your destination table using a Bulk Copy. I have used BulkCopy for loading large datatables with thousands of rows, into Sql tables with great success in terms of performance.
You may want to experiment with BulkCopy in your C# or VB.Net code.
something like this?
DECLARE maxval, val, #ind INT;
SELECT MAX(ID) as maxval FROM table;
while (ind <= maxval ) DO
select `value` as val from `table` where `ID`=ind;
CALL fn(val);
SET ind = ind+1;
end while;
You can do something like this
Declare #min int=0, #max int =0 --Initialize variable here which will be use in loop
Declare #Recordid int,#TO nvarchar(30),#Subject nvarchar(250),#Body nvarchar(max) --Initialize variable here which are useful for your
select ROW_NUMBER() OVER(ORDER BY [Recordid] ) AS Rownumber, Recordid, [To], [Subject], [Body], [Flag]
into #temp_Mail_Mstr FROM Mail_Mstr where Flag='1' --select your condition with row number & get into a temp table
set #min = (select MIN(Rownumber) from #temp_Mail_Mstr); --Get minimum row number from temp table
set #max = (select Max(Rownumber) from #temp_Mail_Mstr); --Get maximum row number from temp table
while(#min <= #max)
BEGIN
select #Recordid=Recordid, #To=[To], #Subject=[Subject], #Body=Body from #temp_Mail_Mstr where Rownumber=#min
-- You can use your variables (like #Recordid,#To,#Subject,#Body) here
-- Do your work here
set #min=#min+1 --Increment of current row number
END
You always don't need a cursor for this. You can do it with a while loop. You should avoid cursors whenever possible. While loop is faster than cursors.