frequently DELETE in Stored Procedure - sql

How I can create a stored procedure and use frequently query like this:
SET NOCOUNT ON;
DECLARE #r INT;
SET #r = 1;
WHILE #r > 0
BEGIN
BEGIN TRANSACTION;
DELETE TOP (100000)
dbo.table1
WHERE Create_Date < DATEADD(YEAR, -5, GETDATE());
SET #r = ##ROWCOUNT;
COMMIT TRANSACTION;
CHECKPOINT;
END
in my new stored procedure?
Thanks for Your answers.

You can make your DELETE statements dynamic using something like below:
CREATE PROCEDURE dbo.DeleteRows (
#tableName VARCHAR(50),
#timestampColName VARCHAR(100),
#since DATETIME2,
#rows INT = 100000
AS
BEGIN
SET NOCOUNT ON;
DECLARE #r INT;
SET #r = 1;
WHILE #r > 0
BEGIN
-- SQL injection might be a problem if table and column name are not coming from a trustworthy source (i.e. user input)
DECLARE #SQL = N'
DELETE TOP (' + CAST(#Count AS INT) + ')' + #tableName + '
WHERE ' + #timestampColName + ' < #since;'
EXEC sp_executesql #SQL, N'#since DATETIME', #since = #since
SET #r = ##ROWCOUNT;
END
END
SQL injection can be tackled using one of the techniques indicated in this question and its answers.

Related

How can i reproduce this procedure to CQL (Cassandra)?

i have this procedure and now some how i need reproduce this procedure from SQL, to CQL, can you please help me? If Cassandra have same as procedures in SQL or not? And if not, how i can set variables for this?
CREATE PROCEDURE [dbo].[sp_DeleteOldTransactionLogs]
#p_daysback INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE
#r int
,#i int
,#maxLogId int
,#billion int = 1000000000
,#deleted int;
SET #r = 1;
SET #i = 1;
SET #deleted = 0;
SELECT
#maxLogId = MAX(ictransactionlogid)
FROM dbo.ictransactionLog WITH (INDEX(IX_ictransactionLog_time))
WHERE
time < DATEADD(day, #p_daysback, GETDATE());
WHILE #r > 0 AND #i <= 7
BEGIN
DELETE TOP (5000) -- this will change
dbo.ictransactionLog
WHERE ictransactionlogid <= #maxLogId;
SET #r = ##ROWCOUNT;
SET #deleted = #deleted + #r;
set #i = #i + 1;
END
SELECT #maxLogId = IDENT_CURRENT( 'ictransactionLog' );
IF #maxLogId > #billion
BEGIN
delete from ictransactionLog
where ictransactionlogid < 500000000
DBCC CHECKIDENT ('ictransactionLog', RESEED, 0);
END
SELECT #deleted;
END
No, it's not possible to do that in the CQL. There is only limited for user-defined functions & user-defined aggregates, and even they are quite limited.
You need to implement that as a code in some language, such as, Python, etc.

Execute dynamic query only to get affected row count

I want to execute a dynamic query to get the affected row count. But SQL Result pane returns me the result after executing it. How to avoid returning the columns. I tried the below way.
DECLARE #Command NVARCHAR(MAX)= 'SELECT * FROM Product WHERE ID = 12'
DECLARE #Count AS INT
EXEC sp_executesql #Command, N'#C INT OUTPUT', #C=#Count OUTPUT
IF (#Count > 0)
BEGIN
EXECUTE (#Command)
END
ELSE
BEGIN
DECLARE #CatalogProduct VARCHAR(MAX) = 'SELECT p.ManufactureCode,p.PartNo,p.Size,p.ID AS ProductID,p.Name ,p.ParentProductID,p.BasePrice FROM Product.Product p WHERE p.ThruDate > GETDATE() '+#Where
EXECUTE (#CatalogProduct)
END
END
I want to avoid returning the null column set from the above attached image.
You can turn off the display, but I think a better approach is to get the count you want directly:
DECLARE #Command NVARCHAR(MAX)= 'SELECT * FROM Product WHERE ID = 12';
DECLARE #count AS INT;
DECLARE #CntCommand NVARCHAR(MAX);
SET #CntCommand = 'SELECT #count = COUNT(*) FROM (' + #Command + ') x)';
EXEC sp_executesql #CntCommand, N'#count INT OUTPUT', #count=#count OUTPUT;
Why not simply?
IF (SELECT COUNT(*) FROM Product = 12) > 0 BEGIN...
I can't see why the COUNT statement needs to be dynamic; there's nothing dynamic about it.
Also, having the SQL '... WHERE p.ThruDate > GETDATE() '+#Where is a terrible idea. If #where is a parameter it'll be wide open to SQL injection.
Try this one. Returns number of rows affected by the last query:
select ##Rowcount
DECLARE #Command NVARCHAR(MAX)= 'SELECT * FROM Product WHERE ID = 12'
DECLARE #CountCommand NVARCHAR(MAX)= 'SELECT #Count=count(1) FROM Product WHERE ID = 12'
DECLARE #Count AS INT
EXEC sp_executesql #CountCommand , N'#Count INT OUTPUT', #Count=#Count OUTPUT
IF (#Count > 0)
BEGIN
EXECUTE (#Command)
END
ELSE
BEGIN
DECLARE #CatalogProduct VARCHAR(MAX) = 'SELECT p.ManufactureCode,p.PartNo,p.Size,p.ID AS ProductID,p.Name ,p.ParentProductID,p.BasePrice FROM Product.Product p WHERE p.ThruDate > GETDATE() '+#Where
EXECUTE (#CatalogProduct)
END
END

Renaming table and view from a different database, works in editor but not from stored procedure

I have to run a monthly job in SQL Server to rename a table and a view in a variety of databases. The database names are stored in a table and this procedure loops through them. The table names change monthly, so I am concatenating the table names based on the current date.
This works well to creating the commands.
If I change my EXEC to PRINT and paste the results into a new query window it works great.
BW_Test.dbo.sp_rename 'BW_Test_DataLog_2018_05','BW_Test_DataLog_2018_06';
BW_Test.dbo.sp_rename 'BW_Test_DataLog','BW_Test_DataLog_2018_05';
However when I run the stored procedure it fails with the following error:
ErrorNumber: 2812 ErrorMessage: Could not find stored procedure 'BW_Test.dbo.sp_rename 'BW_Test_DataLog_2018_05','BW_Test_DataLog_2018_06';'
Here is the stored procedure, thanks in advance!
BEGIN
SET NOCOUNT ON;
-- Find month and year to concatenate with table names
DECLARE #RighNow DATE = GETDATE();
DECLARE #LastMonth DATE = DATEADD(MONTH, -1, GETDATE());
DECLARE #RenameView NVARCHAR(500);
DECLARE #RenameTable NVARCHAR(500);
DECLARE #LastMonthsName NVARCHAR(50);
DECLARE #ThisMonthsName NVARCHAR(50);
DECLARE #COUNTER INT = 0;
DECLARE #MAX INT = (SELECT COUNT(*) FROM DatabaseNames)
DECLARE #Machine VARCHAR(50);
--Start Loop here
WHILE #COUNTER < #MAX
BEGIN
SET #Machine = (SELECT DatabaseName
FROM
(SELECT
(ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) [index],
DatabaseName
FROM
DatabaseNames) R
ORDER BY R.[index]
OFFSET #COUNTER ROWS FETCH NEXT 1 ROWS ONLY);
SET #LastMonthsName = CONCAT(#Machine, '_DataLog', '_', YEAR(#LastMonth), '_', FORMAT(MONTH(#LastMonth), '00'));
SET #ThisMonthsName = CONCAT(#Machine, '_DataLog', '_', YEAR(#RighNow), '_', FORMAT(MONTH(#RighNow), '00'));
SET #RenameView = CONCAT(#Machine, '.dbo.sp_rename ', char(39), #LastMonthsName, char(39), ',', char(39), #ThisMonthsName, char(39), ';');
SET #RenameTable = CONCAT(#Machine, '.dbo.sp_rename ', char(39), #Machine, '_DataLog', char(39), ',', char(39), #LastMonthsName, char(39), ';');
BEGIN TRY
--IMPORTANT - Change the View first or you will have duplicate table names
EXEC #RenameView
EXEC #RenameTable
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
SET #COUNTER = #COUNTER + 1
END
END
Change
EXEC #RenameView
EXEC #RenameTable
to:
EXEC (#RenameView)
EXEC (#RenameTable)
The problem is that EXEC has actually 2 different implementations, one for dynamic SQL (with parenthesis) and another for procedures (without).

Database version

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

MS SQL Stored Procedure for counting Dynamic table items

I had wrote this below stored procedure and getting incorrect statement.
ALTER PROCEDURE dbo.[Counter]
#TableName VARCHAR(100)
AS
BEGIN
DECLARE #Counter INT
DECLARE #SQLQ VARCHAR(200)
SET NOCOUNT ON;
--SET #TableName = 'Member';
SET #SQLQ = 'SELECT COUNT(*) FROM dbo.[' + #TableName + ']';
--Preparing the above sql syntax into a new statement(get_counter).
--Getting an error here I had googled the prepare statement but don't know why facing this error.
PREPARE get_counter FROM #SQLQ;
#Counter = EXEC get_counter; -- here #resutl gets the value of the count.#TableName
DEALLOCATE PREPARE get_counter; -- removing the statement from the memory.
END
Then I had wrote another one:
ALTER PROCEDURE dbo.[Counter]
#TableName VARCHAR(100)
AS
BEGIN
DECLARE #Counter INT
DECLARE #SQLQ VARCHAR(200)
SET NOCOUNT ON;
--SET #TableName = 'Member';
SET #SQLQ = 'SELECT COUNT(*) FROM dbo.[' + #TableName + ']';
--Preparing the above sql syntax into a new statement(get_counter).
Execute #SQLQ; -- here #resutl gets the value of the count.#TableName
--DEALLOCATE PREPARE get_counter; -- removing the statement from the memory.
Return #Counter;
END
It is running fine but I can't get the result in the Counter , anyone please help me(I know that I haven't assigned any value to the counter but if I do I get error).
After your answer martin I had replace my code with yours now its :
ALTER PROCEDURE dbo.[Counter] #SchemaName SYSNAME = 'dbo' , #TableName SYSNAME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLQ NVARCHAR(1000)
DECLARE #Counter INT;
SET #SQLQ = 'SELECT #Counter = COUNT(*) FROM ' +
Quotename(#SchemaName) + '.' + Quotename(#TableName);
EXEC sp_executesql
#SQLQ ,
N'#Counter INT OUTPUT',
#Counter = #Counter OUTPUT
Return SELECT #Counter
END
Now I had retrieved it .
ALTER PROCEDURE dbo.[CreateBusinessCode]
#MemberID bigint,
#len int,
#RewardAccountID bigint,
#ConnectionStatusID tinyint,
#Assign smalldatetime
AS
BEGIN
SET NOCOUNT ON;
DECLARE #counter INT
EXEC #counter = dbo.Counter 'dbo','member';
Select #counter;
END
You should use SYSNAME for object identifiers and Quotename rather than concatenating the square brackets yourself.
ALTER PROCEDURE dbo.[Counter] #TableName SYSNAME,
#SchemaName SYSNAME = 'dbo'
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLQ NVARCHAR(1000)
DECLARE #Counter INT;
SET #SQLQ = 'SELECT #Counter = COUNT(*) FROM ' +
Quotename(#SchemaName) + '.' + Quotename(#TableName);
EXEC sp_executesql
#SQLQ ,
N'#Counter INT OUTPUT',
#Counter = #Counter OUTPUT
SELECT #Counter
END
In SQL Server, if you want to get a result into a variable, you have two choices.
The first is to use cursors.
The second is to do dynamic SQL:
declare #sql varchar(max) = whatever;
declare #cnt int;
declare #cntTable table as (cnt int);
insert into #cntTable
exec(#sql);
select #cnt = t.cnt
from #cntTable
It is cumbersome, but one or the other does work.
try this one.It doesnt query the actual table but will provide you the count of rows.This is better way if you have large tables and you need approximate count.
ALTER PROCEDURE dbo.[Counter]
#TableName VARCHAR(100)
AS
begin
declare #objectid int,#counter int
select #objectid = object_id from sys.all_objects where name = #tablename and schema_id=SCHEMA_ID('dbo')
select #counter = sum(rows) from sys.partitions where object_id= #objectid
and index_id in (0,1)
select #counter
end
go