I am wanting to create a dynamic delete and insert stored procedure for my archiving task in SQL Server.
Here are the temp tables I created:
enter image description here
The source table contains the table names where the records came from. Those records will be inserted in the tables listed in table 2 (the destination table names). Countdays table contains the days for the records I want to archive.
Here's my code below.
DECLARE #i int
DECLARE #destinationTbl NVARCHAR(2048)
DECLARE #sourcetbl NVARCHAR(2048)
DECLARE #cntdays INT
DECLARE #monthAgo dateTime
SET #i = 1
SET #destinationTbl = (SELECT Destination FROM #Table2 ID = #i)
SET #sourcetbl = (SELECT Source FROM #Table1 WHERE ID = #i)
SET #cntdays = (SELECT CountDays FROM Table3 WHERE ID = #i)
SET #monthAgo = DATEADD(month,-#cntdays, GETDATE())
SELECT #arctable
SELECT #sourcetable
SELECT #cntdays
-------ERROR PART----------------------------------------------
INSERT INTO #destinationTbl
SELECT TOP 10 * fROM #sourcetbl Where Createdttm < #monthAgo
---------------------------------------------------------------
After running I keep getting error
Must declare the scalar variable for #destinationTbl, #sourcetbl and "#monthAgo".
I'm still stuck in the inserting part. I have not yet created a script for Deleting the records. I think if someone can help me with the insert script, deleting will be easy.
SQL isn't a scripting language; you can't use a variable to replace something that need to be a literal. The statement INSERT INTO #destinationTbl will try to INSERT values into the table variable #destinationTbl not the table that has the same name as the value in the scalar variable #destinationTbl.
When using dynamic SQL, you need to safely inject the values into the query and pass the other variables as parameters:
DECLARE #i int;
DECLARE #destinationTbl sysname; --Changed datatype, an object cannot have 2048 characters in its name
DECLARE #sourcetbl sysname; --Changed datatype, an object cannot have 2048 characters in its name
DECLARE #cntdays INT; --This isn't used, only SET, is it needed?
DECLARE #monthAgo datetime;
SET #i = 1;
SET #destinationTbl = (SELECT Destination FROM #Table2 ID = #i);
SET #sourcetbl = (SELECT Source FROM #Table1 WHERE ID = #i);
SET #cntdays = (SELECT CountDays FROM Table3 WHERE ID = #i);
SET #monthAgo = DATEADD(month,-#cntdays, GETDATE());
PRINT #arctable;
PRINT #sourcetable;
PRINT #cntdays;
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'INSERT INTO dbo.' + QUOTENAME(#destinationTbl) + #CRLF + --Assumed schema
N'FROM dbo.' + QUOTENAME(#sourcetbl) + #CRLF + --Assumed schema
N'WHERE Createdttm < #monthAgo;';
--PRINT #SQL; --Your best friend.
EXEC sys.sp_executesql #SQL, N'#monthAgo datetime', #monthAgo;
i want to create a new table that has dynamic number of columns depending on the row values of an other table.
For example i have a table (table1) that has 2 columns named 'VALUE' and 'ISACTIVE' ('ISACTIVE' column takes the value 1 if we need to take into account this value as a column in the new table) and i need to create a new table that has:
number of columns (and column name) of new table = the values of table1 where Isactive = 1.
Try out the below.This is assuming all the columns to be integer .we can modify accordingly if it is varchar .We need to alter the existing table and add a column called textval which defaults to '0' here
drop table test
create table test
(
value integer,
isactive integer
);
alter table test add textval nvarchar(max) default '0'
insert into test (value,isactive) values (123,5);
select * from test;
Now update the new columns based on the value of isactive .if it is 5 the new column will have upto col5 all beign integer and use this to create a new table
begin
declare #i integer;
set #i=1
declare #isactive integer;
declare #stmt nvarchar(max);
declare #stmt2 nvarchar(max);
declare #testval nvarchar(max);
set #isactive= (select isactive from test)
while (#i <=#isactive)
begin
declare #textval nvarchar(max);
set #textval = (select textval from test)
set #stmt= 'update test set textval = '''+ #textval +'col' +cast(#i
as varchar(100)) + ' ' + 'integer,'''
execute sp_executesql #statement=#stmt
set #i=#i+1;
end
set #testval=(select left(replace(textval,'0col1','col1'),len(textval)-2)
from test)
set #stmt2 ='create table tab1 ( ' + #testval + ' )';
execute sp_executesql #statement=#stmt2;
end
My first thought was to create it dynamically in a procedure based on your conditions. Read this question and answers , it will help.
T-SQL How to create tables dynamically in stored procedures?
Raw example
DECLARE #SQLString NVARCHAR(MAX)
SET #SQLString = N'CREATE TABLE <table_name> ('
-- Conditons here
SET #SQLString = #SQLString + '<column_name> <type>'
-- End of conditions
SET #SQLString = #SQLString + ')'
EXEC (#SQLString)
SELECT [attributeName] INTO [DatabaseName].[dbo].[NewTableName]
FROM [DatabaseName].[dbo].[FromTableName] WHERE ISACTIVE=1
I'm facing deadlock
was deadlocked on lock resources with another process and has been
chosen as the deadlock victim.
problem In SQL-Server as i'm inserting data in database by picking max id against a specific column then add a increment got the value against which record will be inserted.
i'm calling a procedure as code mentioned below:
CREATE
PROCEDURE [dbo].[Web_GetMaxColumnID]
#Col_Name nvarchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
DECLARE #MaxID BIGINT;
SET NOCOUNT ON;
-- Insert statements for procedure here
BEGIN
BEGIN TRAN
SET #MaxID = (
SELECT Col_Counter
FROM Maintenance_Counter WITH (XLOCK, ROWLOCK)
WHERE COL_NAME = #Col_Name
)
UPDATE Maintenance_Counter
SET Col_Counter = #MaxID + 1
WHERE COL_NAME = #Col_Name
COMMIT
END
SELECT (
CONVERT(
VARCHAR,
(
SELECT office_id
FROM Maintenance
)
) + '' + CONVERT(VARCHAR, (#MaxID))
) AS MaxID
END
any one help me out .....
As Marc already answered, use SEQUENCE. It's available in all supported versions of SQL Server, ie 2012 and later. The only reason to avoid it is targeting an unsupported version like 2008.
In this case, you can set the counter variable in the same statement you update the counter value. This way, you don't need any transactions or locks, eg:
declare #counterValue bigint
UPDATE Maintenance_Counter
SET Col_Counter = Col_Counter + 1 , #counterValue=Col_Counter+1
WHERE COL_NAME = #Col_Name
select #counterValue
Yo can use sequences to generate incremental values avoiding any blocking.
I have adapted my own Counter Generator to be a direct replacement for yours. It creates dynamically the SQL statements to manage sequences, if a Sequence doesn't exist for the value we are looking for, it creates it.
ALTER PROCEDURE [dbo].[Web_GetMaxColumnID]
#Col_Name nvarchar(50)
AS
declare #Value bigint;
declare #SQL nvarchar(64);
BEGIN
if not exists(select * from sys.objects where object_id = object_id(N'dbo.MY_SEQUENCES_' + #Col_Name) and type = 'SO')
begin
set #SQL = N'create sequence dbo.MY_SEQUENCES_' + #Col_Name + ' as bigint start with 1';
exec (#SQL);
end
set #SQL = N'set #Value = next value for dbo.MY_SEQUENCES_' + #Col_Name;
exec sp_executesql #SQL, N'#Value bigint out', #Value = #Value out;
select #Value ;
END
The only inconvenience is that your values can get gaps within (because you could have retrieved a value but finally not used it). This is not a problem on my tables, but you have to consider it.
I have many tables that need ID scramblers, so:
CREATE PROCEDURE SP_generateUniqueID ( -- pass table here somehow -- )
AS
BEGIN
DECLARE #ID varchar(100) -- NEW ID.
DECLARE #isIDInUse tinyint -- BOOLEAN YES/NO.
SET #isIDInUse=1
WHILE(#isIDInUse=1) -- KEEP GENERATING TILL YOU FIND ONE:
BEGIN
SET #ID= dbo.generateID('aA1a1') -- GENERATES ID. doesn't matter how.
IF (#ID NOT IN (#passedTable)) -- DOES #ID EXIST ALREADY?
/*(SEARCHES THE PASSED TABLE! Which its size will be 1XN)*/
SET #isIDInUse=0 -- NO, YOU CAN USE.
END
RETURN #ID
END
I can't make the passing of the existing table go smoothly...
I want to be able to insert any table that uses IDs.
Any suggestion?
I would advise you REALLY look hard into better solutions for this issue. You will be hitting your table/index with every iteration of the new ID that you generate. What is wrong with an auto-incrementing integer value:
create table IDs (ID int identity(1,1))
(also, SQL Server has bit data types for boolean values. No need for your tinyint)
That aside, the only way I think you can do this your way is with dynamic SQL. Using the script below you should be able to see how you can pass in your schema.table to the stored procedure and within the procedure define your ID to be inserted in to the checking loop:
create table a(ID nvarchar(100)) insert into a values('1'),('2'),('3'),('4'),('5')
create table b(ID nvarchar(100)) insert into b values('6'),('7'),('8'),('9'),('10')
declare #Table nvarchar(100) = 'dbo.a'
declare #ID nvarchar(100) = '6'
declare #IDinUse bit = 0
declare #sql nvarchar(max) = 'if exists(select ID from ' + #Table + ' where ID = #ID) select #IDinUse = 1 else select #IDinUse = 0'
exec sp_executesql #sql, N'#ID nvarchar(100), #IDinUse bit output', #ID = #ID, #IDinUse = #IDinUse output
select #IDinUse as IDinUse
go
declare #Table nvarchar(100) = 'dbo.b'
declare #ID nvarchar(100) = '6'
declare #IDinUse bit = 0
declare #sql nvarchar(max) = 'if exists(select ID from ' + #Table + ' where ID = #ID) select #IDinUse = 1 else select #IDinUse = 0'
exec sp_executesql #sql, N'#ID nvarchar(100), #IDinUse bit output', #ID = #ID, #IDinUse = #IDinUse output
select #IDinUse as IDinUse
We're maintaining (and occasionally debugging) a large in-house system. The system has 20+ databases, and a number of servers interfacing to other systems, processing data, etc. Not all is in-house developed - i.e. we don't always have access to source code.
At one place, we can see the system creating a #temp table - and then, in the next step, failing due to a data-error. We can see the existence of the #temp table in Management Studio - it exists in tempdb --> Temporary Tables as something like
#MyStuff________________________________________________________________________________________________________000000A65029
Obviously, the context menu here doesn't offer the full functionality (with Create table, select top 1000, etc.) - but only Reportsand Refresh.
I can find the table in sys.objects, sys.tables and even its column definition in sys.columns.
The question is: Is it in any way possible to access the data in the table? We can break execution so as to ensure that the table stays in scope, so the table vanishing shouldn't be an issue. This is not something that is to be done regularly or in code - it's more or less a one-shot deal. (I hope).
Unwieldy but you can examine the tables pages from an admin logon.
Get object id;
select object_id from tempdb.sys.tables where name like '#mystuff%'
Get a list of allocated pages;
dbcc ind('tempdb', <object id>, -1)
for each of the PageFID / PagePID (file/page IDs)
dbcc traceon(3604);
dbcc page(tempdb, <PageFID>, <PagePID>, 3) with tableresults
If I create #mystuff from another session I can see in a dbcc page view from my own session:
Slot 0 Offset 0x60 Length 18 Slot 0 Column 1 Offset 0xb Length 7 Length (physical) 7 myFieldName MyValue
If you prefix a temporary table name with two octothorpes, e.g. ##mystuff, it creates a global temporary table that exists outside session scope.
That does require you to be able to alter the query text, which may or may not be accessible in this specific case.
Based on Alex K's answer I wrote up a script. I didn't actually end up with all the rows from the table, so there's likely room for improvement.
Disclaimer: As with any code you find online... understand it before you run it and use at your own risk. Ends with DBCC TRACEOFF(3604).
/* Run this first to get your object_id. In the case of > 1, choose one based on date and rowcount. */
DECLARE #tableName NVARCHAR(100) = '#conv'
SELECT #tableName TableName,
o.object_id,
SUM(p.rows) TableRows,
o.Create_Date CreateDatetime,
o.name TempDBFullName
FROM tempdb.sys.objects o
LEFT JOIN tempdb.sys.partitions p ON o.object_id = p.object_id
AND p.index_id <= 1
WHERE o.name LIKE #tableName+'\_\_\_%' ESCAPE '\'
GROUP BY o.name, o.object_id,o.Create_Date
--(-1074969413)
GO
/* Run this using the object_id from above to get table contents */
DECLARE
#object_id INT = -1074969413, --from above
#tableName NVARCHAR(100),
#rows BIGINT,
#created DATETIME,
#msg NVARCHAR(MAX),
--Set this to NULL for all pages
#max_pages INT = 2 --set this to restrict how many pages are read. Must be > 1.
IF #max_pages < 2
RAISERROR('You''re going to need some more pages there, Sport. Try at least 2.',16,1)
RAISERROR('
**** This code uses an undocumented feature and is for informational purposes only. Do not assume results are complete or correct. ****
',0,1)
SET NOCOUNT ON
--declare #tablename nvarchar(100), #msg nvarchar(max), #rows bigint, #created datetime
SELECT #rows = SUM(rows) FROM tempdb.sys.partitions WHERE object_id = #object_id AND index_id <= 1
SELECT #rows = ISNULL(#rows,0)
SELECT #tableName = SUBSTRING(o.name,1,CHARINDEX('____',o.name,1)-1),
#created = o.create_date
FROM tempdb.sys.objects o
WHERE o.object_id = #object_id
SELECT #msg = 'Object name '+QUOTENAME(#tableName)+' is expected to contain up to '+CAST(#rows AS NVARCHAR(20))+' rows and was created # '+CONVERT(NVARCHAR,#created,113)+'.'
RAISERROR(#msg,0,1) WITH NOWAIT
DROP TABLE IF EXISTS #dbccind
CREATE TABLE #dbccind(PageFID NVARCHAR(100),PagePID NVARCHAR(100),IAMFID NVARCHAR(100),IAMPID NVARCHAR(100),ObjectID NVARCHAR(100),IndexID NVARCHAR(100),PartitionNumber NVARCHAR(100),PartitionID NVARCHAR(100),iam_chain_Type NVARCHAR(100),PageType NVARCHAR(100),IndexLevel NVARCHAR(100),NextPageFID NVARCHAR(100),NextPagePID NVARCHAR(100),PrevPageFID NVARCHAR(100),PrevPagePID NVARCHAR(100))
DECLARE #SQL NVARCHAR(MAX) = N'dbcc ind(''tempdb'', '+CAST(#object_id AS NVARCHAR(20))+', -1) WITH NO_INFOMSGS'
--Get a list of pages for this object
INSERT #dbccind
EXEC sp_executesql #SQL
--add an iterative counter for following loop
ALTER TABLE #dbccind ADD ID INT IDENTITY NOT NULL
--select '#dbccind' [#dbccind], * FROM #dbccind
--DECLARE #SQL nvarchar(max), #msg nvarchar(max)
DROP TABLE IF EXISTS #TempTableUnpivoted
CREATE TABLE #TempTableUnpivoted(ParentObject NVARCHAR(100), Object NVARCHAR(100), Field NVARCHAR(100), Value NVARCHAR(1000))
DBCC TRACEON(3604) WITH NO_INFOMSGS;
--Use DBCC PAGE to dump TEMPDB pages to a table as name/value pairs
IF #max_pages IS NOT NULL
BEGIN
SELECT #msg = 'Max pages set. This process will read (at most) '+CAST(#max_pages AS NVARCHAR(20))+' data page(s).'
RAISERROR(#msg,0,1) WITH NOWAIT
END
DECLARE #i INT = 1, #j INT = (SELECT MAX(id) FROM #dbccind)
WHILE #i <= #j
AND (#i <= #max_pages OR #max_pages IS NULL)
BEGIN
SELECT #SQL = 'INSERT #TempTableUnpivoted EXEC SP_EXECUTESQL N''DBCC PAGE(TEMPDB, '+CAST(PageFID AS NVARCHAR(20))+', '+CAST(PagePID AS NVARCHAR(20))+', 3) WITH TABLERESULTS, NO_INFOMSGS'''
FROM #dbccind
WHERE id = #i
SELECT #msg = 'Reading page '+CAST(#i AS NVARCHAR(20))+' of '+CAST(#j AS NVARCHAR(20))+' # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
--raiserror(#sql,0,1) with nowait
IF #SQL IS NULL RAISERROR('Unable to read pages.',16,1) WITH NOWAIT
EXEC SP_EXECUTESQL #SQL
SELECT #i += 1--, #SQL = NULL, #msg = NULL
END
IF #max_pages <= #i
BEGIN
SELECT #msg = 'Max pages reached. This process has read data from up to '+CAST(#max_pages AS NVARCHAR(20))+' data page(s).'
RAISERROR(#msg,0,1) WITH NOWAIT
END
--SELECT '#TempTableUnpivoted' [#TempTableUnpivoted], * FROM #TempTableUnpivoted
--get a list of fields from the page results. Assume all occur withing the first 1000 results.
SELECT #msg = 'Identify fields # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
DROP TABLE IF EXISTS #TableFields
CREATE TABLE #TableFields(FieldName NVARCHAR(100) NOT NULL PRIMARY KEY, ColumnId INT NOT NULL)
INSERT #TableFields(FieldName,ColumnId)
SELECT DISTINCT Field,ColumnId
FROM (SELECT TOP 1000 *, SUBSTRING(Object,CHARINDEX('Column ',Object,1)+7,CHARINDEX(' Offset ',Object,1)-CHARINDEX('Column ',Object,1)-7) ColumnId
FROM #TempTableUnpivoted
WHERE ParentObject LIKE 'Slot %'
AND Object LIKE 'Slot%Column%offset%length%') a
--Set up variables to hold a list of column names
SELECT #msg = 'Compile list of field names # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
--declare #sql nvarchar(max)
DECLARE #columnlist NVARCHAR(MAX) = (SELECT STRING_AGG(QUOTENAME(FieldName,''''),',') WITHIN GROUP ( ORDER BY ColumnId) FROM #TableFields)
DECLARE #columnlistquoted NVARCHAR(MAX) = (SELECT STRING_AGG(QUOTENAME(FieldName),',') WITHIN GROUP ( ORDER BY ColumnId) FROM #TableFields)
DECLARE #columnlistTyped NVARCHAR(MAX) = (SELECT STRING_AGG(QUOTENAME(FieldName),' nvarchar(1000),') WITHIN GROUP ( ORDER BY ColumnId) FROM #TableFields)+' nvarchar(1000)'
IF #columnlist IS NULL OR #columnlistquoted IS NULL OR #columnlistTyped IS NULL
BEGIN
SELECT #columnlist [#columnlist], #columnlistquoted [#columnlistquoted], #columnlistTyped [#columnlistTyped]
RAISERROR('Unable to compile columns. You might need to read more pages to get a solid list. Try incrementing #max_pages or clear #max_pages to do a full read.',16,1) WITH NOWAIT
END
--Create a list of unpivoted name/value pairs (NVP) for just the columns we need. This _may_ be able to be cut out with implicit restrictions in the pivot.
SELECT #msg = 'Reduce unpivoted data to name/value pairs # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
DROP TABLE IF EXISTS #TempTableNVP
CREATE TABLE #TempTableNVP(ParentObject NVARCHAR(100),Field NVARCHAR(100),Value NVARCHAR(1000))
SELECT #SQL = N'
SELECT ParentObject,Field,Value
FROM #TempTableUnpivoted
WHERE Field in ('+#columnlist+')'
INSERT #TempTableNVP(ParentObject,Field,Value)
EXEC sp_executesql #SQL
--Pivot data to final form to match temp table
SELECT #msg = 'Add fields to working table 1 # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
DROP TABLE IF EXISTS #TempTable
CREATE TABLE #TempTable(id INT IDENTITY NOT NULL)
SELECT #SQL = 'ALTER TABLE #TempTable ADD '+#columnlistTyped
IF #SQL IS NULL
RAISERROR('Unable to alter working table 1.',16,1) WITH NOWAIT
--raiserror(#sql,0,1) with nowait
EXEC sp_executesql #SQL
--select '#TempTable' [#TempTable], * from #TempTable
SELECT #msg = 'Pivot data to get original table # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
SELECT #SQL = 'INSERT #TempTable('+#columnlistquoted+')
SELECT '+#columnlistquoted+'
FROM (SELECT Field,Value,ParentObject FROM #TempTableNVP) a
PIVOT (MIN(Value) FOR Field IN ('+#columnlistquoted+')) PVT'
--raiserror(#sql,0,1) with nowait
IF #SQL IS NULL RAISERROR('Unable to populate working table 1.',16,1) WITH NOWAIT
EXEC sp_executesql #SQL
--Return results
SELECT #msg = 'Return results # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
--SELECT * FROM #TempTable
SELECT #SQL = 'SELECT '+#columnlistquoted+' FROM #TempTable'
EXEC sp_executesql #SQL
DBCC TRACEOFF(3604) WITH NO_INFOMSGS;