I have the following T-SQL pseudo-code query
SET #Loop = 1
WHILE #Loop > 0
BEGIN
IF (#Table_name = 'abcdef')
BEGIN
SET #SqlCmd = 'update top 1000 #Table_name
set columnA = ''haha'''
END
ELSE
BEGIN
SET #SqlCmd = 'update top 1000 #Table_name
set columnA = ''hehe'''
END
EXEC dbo.sp_executesql #SqlCmd
SET #Loop = ##RowCount
END
Now, assume the both #Table_name (abcdef and the other one) has 5000 records, the update statement inside the "ELSE" clause would just run 5 times and get out of the loop. But, the update statement inside the "IF" clause is falling into an infinite loop. I believe the cause is ##RowCount being interrupted by the IF (#Table_name = 'abcdef'). because ##RowCount always return 1 when running inside that block.
I have a quick (but really ugly) solution for this as follow:
SET #Loop = 1
WHILE #Loop > 0
IF (#Table_name = 'abcdef')
BEGIN
SET #SqlCmd = 'update top 1000 #Table_name
set columnA = ''haha'''
SELECT #Loop = COUNT(1) FROM #Table_name WHERE columnA != 'haha'
END
ELSE
BEGIN
SET #SqlCmd = 'update top 1000 #Table_name
set columnA = ''hehe'''
SET #Loop = ##RowCount
END
EXEC dbo.sp_executesql #SqlCmd
END
Although the above solution would work, is there a better way to solve the problem? Eager to learn more, thanks!
Your problem isn't ##ROWCOUNT. It is:
update top 1000 #Table_name
set columnA = 'haha';
If you run this in a loop, it is going to set the same rows over and over. Perhaps you should use:
update top 1000 #Table_name
set columnA = 'haha'
where columnA <> 'haha';
Related
I'm running a Restore Verify Only loop through a table, but I need it to UPDATE a field in that table if it's successful. Here is my code:
DECLARE #Path NVARCHAR(max)
DECLARE #DatabaseName NVARCHAR(100)
DECLARE #NSql NVARCHAR(1000)
DECLARE #Update NVARCHAR(200)
DECLARE #DB_Text NVARCHAR(50)= 'Backup has been confirmed for'
BEGIN
DECLARE
#DatabaseId INT = 1,
#NumberOfDBs INT
SELECT #NumberOfDBs= COUNT(*) FROM dbo.RestoreVerifyDatabases
WHILE #DatabaseId<= #NumberOfDBs
BEGIN
SELECT * FROM dbo.RestoreVerifyDatabases WHERE DatabaseId= #DatabaseId
SET #DatabaseId = #DatabaseId + 1
END
SET #Path = (SELECT LastBackupFileName FROM RestoreVerifyDatabases WHERE DatabaseId =
#DatabaseId)
SET #DatabaseName = (SELECT DatabaseName FROM RestoreVerifyDatabases WHERE DatabaseId =
#DatabaseId)
SET #NSql = N'SELECT LastBackupFileName
FROM RestoreVerifyDatabases
WHERE DatabaseName = #DatabaseName
AND DatabaseId = #DatabaseId'
EXEC sp_executesql #NSql
IF #DatabaseId IS NULL
BEGIN
RAISERROR(N'Verify failed. Backup information for database N''#DatabaseName'' not
found.', 16, 1)
END
RESTORE VERIFYONLY
FROM #Path
WITH FILE = #DatabaseId, checksum
SET #Update= N'UPDATE RestoreVerifyDatabases
SET Confirmed = #DB_Text + #DatabaseName
WHERE DatabaseID = #DatabaseId'
EXEC sp_executesql #Update
END
The looping and calling of Restore Verify Only works fine but the UPDATE is not getting called. Please help.
There's a few things wrong with your code as far as I can see. As my comment indicated, you run a loop over the #databaseID, and then immediately increase the id with 1 within the loop.
This means your #databaseId simply will increase until it reaches the maximum value of the id, and then exits the loop. The rest of your code will then execute once (only for that ID).
That happens here in your code:
SELECT #NumberOfDBs= COUNT(*) FROM dbo.RestoreVerifyDatabases
WHILE #DatabaseId<= #NumberOfDBs
BEGIN
SELECT * FROM dbo.RestoreVerifyDatabases WHERE DatabaseId= #DatabaseId
SET #DatabaseId = #DatabaseId + 1
END
-- Bunch of other code
So #databaseID goes from 1 to 50 (just a random number) within that loop, then exists with a value of 50. None of the underlying code ever gets to see any value other than 50.
To fix that, the code should look like this:
SELECT #NumberOfDBs= COUNT(*) FROM dbo.RestoreVerifyDatabases
WHILE #DatabaseId<= #NumberOfDBs
BEGIN
SELECT * FROM dbo.RestoreVerifyDatabases WHERE DatabaseId= #DatabaseId
-- Bunch of other code
SET #DatabaseId = #DatabaseId + 1
END
This would ensure the "bunch of other code" processes #databaseid = 1, then processes #databaseid = 2, etc. until #databaseid = 50.
You can also remove this line:
SELECT * FROM dbo.RestoreVerifyDatabases WHERE DatabaseId= #DatabaseId
I suspect you have it for debugging purposes, but it doesn't really do anything.
These lines can be simplified:
SET #Path = (SELECT LastBackupFileName FROM RestoreVerifyDatabases WHERE DatabaseId =
#DatabaseId)
SET #DatabaseName = (SELECT DatabaseName FROM RestoreVerifyDatabases WHERE DatabaseId =
#DatabaseId)
as:
SELECT #Path = LastBackupFileName, #DatabaseName = DatabaseName FROM RestoreVerifyDatabases WHERE DatabaseId = #DatabaseId
You also have a RAISEERROR. I suspect that is because you do a count(*), rather than selecting actually existing #databaseids. This works in an ideal scenario, where databases never get deleted. In the reasl world, though, you'd go from databaseid 1 to 5, because 2, 3, and 4 were removed. Your count will still be 50 databases, but you'd miss all databases with ids above the count due to these gaps. You'd be trying to process the databaseid that no longer exists, and miss the ones with an id > 50.
You could instead write your loop to do something like the following:
SELECT #DatabaseID = MIN(DatabaseID) FROM dbo.RestoreVerifyDatabases
WHILE #datbaseID IS NOT NULL
BEGIN
-- Do stuff
SELECT #DatabaseID = MIN(DatabaseID) FROM dbo.RestoreVerifyDatabases WHERE databaseID > #databaseID
END
Finally, your dynamic code isn't working. You are adding literal text rather than the value of the parameters. If I print the value of #Update at the end, it shows as:
UPDATE RestoreVerifyDatabases
SET Confirmed = #DB_Text + #DatabaseName
WHERE DatabaseID = #DatabaseId
Your code should be something like:
SET #Update= CONCAT('UPDATE RestoreVerifyDatabases SET Confirmed = ''', #DB_Text, ' ', #DatabaseName, ''' WHERE DatabaseID = ', #DatabaseId)
Which outputs:
UPDATE RestoreVerifyDatabases SET Confirmed = 'Backup has been confirmed for Last' WHERE DatabaseID = 2
I have about 8 tables that have 10 million rows or more each and I want to do the fastest/elegant delete on them. I have decided to delete them in chunks at a time. When I added my changes, it looks very very ugly, and want to know how to format it to look better. Also, is this the best way to be doing this?
DECLARE #ChunkSize int
SET #ChunkSize = 50000
WHILE #ChunkSize <> 0
BEGIN
DELETE TOP (#ChunkSize) FROM TABLE1
WHERE CREATED < #DATE
SET #ChunkSize = ##rowcount
END
DECLARE #ChunkSize int
SET #ChunkSize = 50000
WHILE #ChunkSize <> 0
BEGIN
DELETE TOP (#ChunkSize) FROM TABLE2
WHERE CREATED < #DATE
SET #ChunkSize = ##rowcount
END
.......
I would be doing this for all 8 tables which doesn't seem practical. Any advice on how to clean this up?
Prior to 2016 SP1 when partitioning is only available in Enterprise you can either delete in batches or if the amount of data to be removed is small compared to the total data you can copy the good data to another table.
For doing the batch work I would make some suggestions to your code so it is a bit simpler.
DECLARE #ChunkSize int
SELECT #ChunkSize = 50000 --use select instead of set so ##rowcount will <> 0
WHILE ##rowcount <> 0
BEGIN
DELETE TOP (#ChunkSize) FROM TABLE1
WHERE CREATED < #DATE
END
SELECT #ChunkSize = #ChunkSize --this will ensure that ##rowcount = 1 again.
WHILE ##rowcount <> 0
BEGIN
DELETE TOP (#ChunkSize) FROM TABLE2
WHERE CREATED < #DATE
END
You may have to play with the ChunkSize to work well with your data but 50k is a reasonable starting point.
If you want to avoid repeating your loop for each table, you could use dynamic SQL
IF OBJECT_ID('tempdb..#tableNames') IS NOT NULL DROP TABLE tempdb..#tableNames
SELECT name INTO #tableNames FROM sys.tables WHERE name IN (/* Names of tables you want to delete from */)
DECLARE #table varchar(50)
DECLARE #query nvarchar(max)
WHILE EXISTS (select '1' from #tableNames)
BEGIN
SET #table = (select top 1 name from #tableNames)
DELETE FROM #tableNames WHERE name = #table
SET #query = 'DECLARE #ChunkSize int
SET #ChunkSize = 50000
WHILE #ChunkSize <> 0
BEGIN
DELETE TOP (#ChunkSize) FROM ' + #table + '
WHERE CREATED < #DATE
SET #ChunkSize = ##rowcount
END'
EXEC sp_executesql #query
END
I want to build custom Where condition based on stored procedure inputs, if not null then I will use them in the statement, else I will not use them.
if #Vendor_Name is not null
begin
set #where += 'Upper(vendors.VENDOR_NAME) LIKE "%"+ UPPER(#Vendor_Name) +"%"'
end
else if #Entity is not null
begin
set #where += 'AND headers.ORG_ID = #Entity'
end
select * from table_name where #where
But I get this error
An expression of non-boolean type specified in a context where a condition is expected, near 'set'.
Use this :
Declare #Where NVARCHAR(MAX)
...... Create your Where
DECLARE #Command NVARCHAR(MAX)
Set #Command = 'Select * From SEM.tblMeasureCatalog AS MC ' ;
If( #Where <> '' )
Set #Comand = #Command + ' Where ' + #Where
Execute SP_ExecuteSQL #Command
I tested this and it Worked
You cannot simply put your variable in normal SQL as you have in this line:
select * from table_name where #where;
You need to use dynamic SQL. So you might have something like:
DECLARE #SQL NVARCHAR(MAX) = 'SELECT * FROM Table_Name WHERE 1 = 1 ';
DECLARE #Params NVARCHAR(MAX) = '';
IF #Vendor_Name IS NOT NULL
BEGIN
SET #SQL += ' AND UPPER(vendors.VENDOR_NAME) LIKE ''%'' + UPPER(#VendorNameParam) + ''%''';
END
ELSE IF #Entity IS NOT NULL
BEGIN
SET #SQL += ' AND headers.ORG_ID = #EntityParam';
END;
EXECUTE SP_EXECUTESQL #SQL, N'#VendorNameParam VARCHAR(50), #EntityParam INT',
#VendorNameParam = #Vendor_Name, #EntityParam = #Entity;
I assume your actual problem is more complex and you have simplified it for this, but if all your predicates are added using IF .. ELSE IF.. ELSE IF, then you don't need dynamic SQL at all, you could just use:
IF #Vendor_Name IS NOT NULL
BEGIN
SELECT *
FROM Table_Name
WHERE UPPER(vendors.VENDOR_NAME) LIKE '%' + UPPER(#Vendor_Name) + '%';
END
ELSE IF #Entity IS NOT NULL
BEGIN
SELECT *
FROM Table_Name
WHERE headers.ORG_ID = #Entity;
END
old question but want to add that i just found using
where
CASE WHEN #id = 0
THEN 1
ELSE id
END
=
CASE WHEN #id = 0
THEN 1
ELSE #id
END
this is from these steps:
where id = #id
check if #id is null (or invalid) or have valid value so replace #id using case:
where id =
CASE WHEN #id is null
THEN 0
ELSE #id
END
this is will end up as
where id = 0 -- if #id is null, still affected your query
where id = #id -- if #id have valid value
because we want use this "where" clause only if have valid data then we change id too
where
CASE WHEN #id is null
THEN 0
ELSE id
END
=
CASE WHEN #id is null
THEN 0
ELSE #id
END
then will end up as
where 0 = 0 -- if #id is null and it doesn't affect your query
where id = #id -- if #id have valid value
don't forget if id is varchar, use 0 as '0'
CASE WHEN #id is null
THEN '0'
ELSE id
END
i use this in my project and working well, even i use more than 1 of this in one query. please let me know if this query costing time at larger data
No need to use ad-hoc query (execute SP_ExecuteSQL)
Check below logic, you can use N number of dynamic / un-sure parameters / conditions
-- flages
declare #chk_vendor bit;
declare #chk_entity bit;
-- setting off
set #chk_entity = 0;
set #chk_vendor = 0;
if #Vendor_Name is not null
begin
set #chk_vendor = 1;
end
else if #Entity is not null
begin
set #chk_entity = 1;
end
SELECT *
FROM table_name
WHERE (UPPER(vendors.VENDOR_NAME) LIKE '%' +
UPPER(#Vendor_Name) + '%' OR #chk_vendor = 0)
AND (headers.ORG_ID = #Entity OR #chk_entity = 0)
I am posting this answer as there many similar questions , remember flag will just enable or disable a condition
SET #count = 0
SET #select = ''
WHILE #count < #c_count
BEGIN
SET #count = #count+1.
SET #select = #select+'cinema'+cast(#count AS VARCHAR)+'+'
END
SET #select = SUBSTRING(#select, 1, LEN(#select) - 1)
select #qty = qty from #qty
SET #buffer = 'UPDATE #table SET total_sales = '+#select
PRINT #buffer
EXEC(#buffer)
update #table set total_quantity = tq.qty from #table t inner join #qty tq on t.pkey =tq.id
here's my code in updating #table, i am having a problem putting the last update in #buffer,
help me pls.
My first question whenever I see someone doing this, is WHY?
If you are building up a SQL String and then trying to execute it, you are probably doing it wrong, and more than likely, you don't know enough of what you are doing that you can prevent SQL injection attacks.
I have to write a procedure which is able using a Table version to bring the database to a specific moment in type. For instance to move from version 1 to version 10 or the other way around. The thing is I'm pretty blurry with this chapter, and the school course has almost nothing about it. I tried using the internet to build a solution but somehow I got stuck. Please help me understand what am I doing wrong.
Table version, 1 columnm, type int
query
create procedure [dbo].[goto_vs] (
#vs int
)
as
begin
declare #current_vs int, #counter int;
declare #sqlquery nvarchar(50); --query to modify
declare #sqlsp nvarchar(30);
declare #sqlversion nvarchar(3);
declare #sqlreverse nvarchar(10);
--get the current version from table
select #current_vs=version from dbo.version;
--checking for valid version
if (#current_vs = #vs) begin
print('The database is already at this version...')
return
end
else begin
if (#vs > 5) begin
print('Setting the version of databse to last one...')
set #vs = 5
end
else begin
if (#vs < 0) begin
print('Setting the database to default...')
set #vs = 0
end
end
end
--setting up the string for exec
set #sqlsp = 'exec sp_create_table_awards'
--check if we go further or earlier in time
print('Changing database version...')
if (#vs > #current_vs) begin
set #sqlreverse = ''
goto upgrading
end
else begin
set #sqlreverse = 'undo_create_awards'
goto downgrading
end
--upgrading code
upgrading:
set #counter = #current_vs + 1
while (#counter <= #vs) begin
set #sqlquery = #sqlsp + cast(#counter as nvarchar(2)) + #sqlreverse
print(#sqlquery)
exec sp_executeSql #sqlquery
set #counter = #counter + 1
end
goto ending
downgrading:
set #counter = #current_vs
while (#counter > #vs) begin
set #sqlquery = #sqlsp + cast(#counter as nvarchar(2)) + #sqlreverse
print(#sqlquery)
exec sp_executeSql #sqlquery
set #counter = #counter - 1
end
goto ending
ending:
update dbo.version set version=#vs
print('Database version changed...')
end
Considering I figured a way, and have got no responses, I will post it as a response because it may help other students which studie computer science
To simplify I named all my procedures by this pattern do_x and undo_x where x is a int where do / undo _x are procedures which pair toughter for example do_1 and undo_1 create a table and destroy a table
ALTER PROCEDURE [dbo].[goto_vs]
-- Add the parameters for the stored procedure here
#v int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
declare #i int
declare #toexec nvarchar(70)
set #i = (select version from version)
-- If the requested version is lower than the current one, downgrade.
while(#i > #v)
BEGIN
set #i = #i - 1
set #toexec = 'Undo_' + CONVERT(varchar,#i);
exec sp_executeSql #toexec
print #toexec
END
-- Otherwise, upgrade.
while(#i < #v)
BEGIN
set #toexec = 'Update_' + CONVERT(varchar, #i);
exec sp_executeSql #toexec
set #i = #i + 1
print #toexec
END
END