So I’ve got a database which maintains all of the data in it in a history database, so that we can change the history date, and go back and look at old data. I need to write a query that adjusts the dates in these history tables for each table. Right now I’ve got it working as a cursor, but it takes several minutes to run, and I want to see if I can do it without a cursor.
Edit: To be clear, the primary keys that I’m pulling are the primary keys for the non-history tables. The history tables may have multiple entries for the single primary key. (Which is why the inner sql is doing the join that it is)
Here’s the cursor:
DECLARE tableID CURSOR FOR
SELECT
OBJECT_NAME(ic.OBJECT_ID) AS TableName,
COL_NAME(ic.OBJECT_ID,ic.column_id) AS ColumnName
FROM sys.indexes AS i
INNER JOIN sys.index_columns AS ic
ON i.OBJECT_ID = ic.OBJECT_ID
AND i.index_id = ic.index_id
WHERE i.is_primary_key = 1
and COL_NAME(ic.OBJECT_ID, ic.column_id) != 'RecordID'
DECLARE @currentTable varchar(100)
DECLARE @currentID varchar(100)
DECLARE @currSql varchar(max)
OPEN tableID
FETCH FROM tableID
INTO @currentTable, @currentID
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @currSql =
'update t1
set t1.EndDate = t2.BeginDate
from hist.' + @currentTable + ' t1 inner join hist.' + @currentTable + ' t2
on t1.' + @currentID + ' = t2.' + @currentID + '
and t2.BeginDate = (select MIN(BeginDate) from hist.' + @currentTable + ' t
where t.BeginDate >= t1.EndDate and t.' + @currentID + ' = t1.' + @currentID + ')'
EXEC(@currSql)
FETCH FROM tableID
INTO @currentTable, @currentID
END
CLOSE tableID
DEALLOCATE tableID
I find it very hard to believe that this runs slowly because it’s a cursor. You can make the cursor slightly more efficient by saying:
…but I bet if you just print all those SQL commands, copy and paste them into a new window, and execute them manually, that it will still take a lot longer than you’d like. The speed is probably related to the amount of data you’re updating (or at least scanning), not because you’re using a cursor to generate the commands.
You can generate these commands without explicitly using a cursor, but rather using the metadata tables to build a string, but this will still really use a cursor in the engine… the code is just a lot tidier. I’ll post an example shortly.
First, just adding a sample of what the output of your query currently looks like, for say the id column on table1. To help illustrate my comment and how it might be very hard for this update to ever affect any rows:
Perhaps you meant a much simpler query, like:
Or perhaps you meant to reference some other table in the subquery?
Anyway assuming one of the above queries is really what you intend to execute, to generate the first query you could try:
And for the second it is a lot simpler:
Note that I changed the >= to > since if it’s already = there’s no reason to update. And again, these assume that everything is in the hist schema and all primary keys are single column primary keys. Though I will state again that the first, longer version of the query is much more expensive (two extra clustered index seeks and a very expensive table spool operator) – while not achieving results that are any different, whatsoever, from the shorter version I posted.