I am doing a search Criteria Form in that i have various types of search options,Instead of doing a If condition in code based on the control select, we can check by the parameter values and update the where condition
Declare #Amount varchar(Max);
SET #Amount= 'and Amount=500';
Select Processor, [Stmt Date], Description, Amount, [Allocation Date],
Entity, URN, [Customer Acc], [Invoice Number],Lamount, DumpEntity as [Received Entity]
from tbl_Employee_Salary
WHERE 1=1 +CONVERT(int,CASE when #Amount IS NOT null then #Amount Else ' ' END)
Conversion failed when converting the varchar value 'and Amount=500'
to data type int.
Warning: This is completely untested.
I hate these types of queries, but what you are after is something like this. I'm not going to explain it, sorry, but it's up to you to understand the below and support it yourself. This is known as a catch-all query or a kitchen sink query.
--All below datatypes are ASSUMED
--Declare a variable for every column (you won't need all of these if they aren't going to all be used)
DECLARE #Processor varchar(50),
#StmtDate date,
#Description varchar(100),
#Amount int,
#AllocationDate date,
#Entity int,
#URN int,
#CustomerAcc int,
#InvoiceNumber int,
#Lamount decimal(10,2),
#DumpEntity varbinary(8);
--Set your values here (I assume this will actually be an SP or something)
SET #Amount = 500;
--Create the initial SQL statement
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SELECT Processor,' + NCHAR(13) + NCHAR(10) +
N' [Stmt Date],' + NCHAR(13) + NCHAR(10) +
N' Description,' + NCHAR(13) + NCHAR(10) +
N' Amount,' + NCHAR(13) + NCHAR(10) +
N' [Allocation Date],' + NCHAR(13) + NCHAR(10) +
N' Entity,' + NCHAR(13) + NCHAR(10) +
N' URN,' + NCHAR(13) + NCHAR(10) +
N' [Customer Acc],' + NCHAR(13) + NCHAR(10) +
N' [Invoice Number],' + NCHAR(13) + NCHAR(10) +
N' Lamount,' + NCHAR(13) + NCHAR(10) +
N' DumpEntity AS [Received Entity]' + NCHAR(13) + NCHAR(10) +
N'FROM tbl_Employee_Salary'
--Now you need to start creating the WHERE
DECLARE #Where nvarchar(MAX);
SET #Where = N'WHERE ' +
NULLIF(STUFF(CASE WHEN #Processor IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND Processor = #Processor' ELSE N'' END +
CASE WHEN #StmtDate IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND [Stmt Date] = #StmtDate' ELSE N'' END +
CASE WHEN #Description IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND [Description] = #Description' ELSE N'' END +
CASE WHEN #Amount IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND Amount = #Amount' ELSE N'' END +
CASE WHEN #AllocationDate IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND [Allocation Date] = #AllocationDate' ELSE N'' END +
CASE WHEN #Entity IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND Entity = #Entity' ELSE N'' END +
CASE WHEN #URN IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND URN = #URN' ELSE N'' END +
CASE WHEN #CustomerAcc IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND [Customer Acc] = #CustomerAcc' ELSE N'' END +
CASE WHEN #InvoiceNumber IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND [Invoice Number] = #InvoiceNumber' ELSE N'' END +
CASE WHEN #Lamount IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND Lamount = #Lamountcessor' ELSE N'' END +
CASE WHEN #DumpEntity IS NOT NULL THEN NCHAR(13) + NCHAR(10) + N' AND DumpEntity = #DumpEntity' ELSE N'' END,1,8,N''),N'');
--Now add the 2 values together
SET #SQL = #SQL + ISNULL(#Where,N'') + N';';
--Create the parameter string
DECLARE #Params nvarchar(MAX);
--All following datatypes are ASSUMED
SET #Params = N'#Processor varchar(50),#StmtDate date,#Description varchar(100),#Amount int,#AllocationDate date, #Entity int,#URN int,#CustomerAcc int,#InvoiceNumber int,#Lamount decimal(10,2),#DumpEntity varbinary(8)'
PRINT #SQL; --Your debugging best friend
--And execute the dynamic SQL
EXEC sp_executesql #SQL, #Params, #Processor, #StmtDate, #Description ,#Amount, #AllocationDate ,#Entity, #URN , #CustomerAcc, #InvoiceNumber, #Lamount, #DumpEntity;
GO
Good luck!
Related
My requirement is to create a global temporary table and store data there which I would access later. I give a dynamic name to my global temporary table and getting error that
Invalid object name '##Tmp1_84'.
84 is #SPID
here is my script. please have a look and tell me what to rectify in code to get rid of runtime error Invalid object name '##Tmp1_84'
CREATE Proc USP_GetValuationValue
(
#Ticker VARCHAR(10),
#ClientCode VARCHAR(10),
#GroupName VARCHAR(10)
)
AS
DECLARE #SPID VARCHAR(MAX)
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SELECT #SPID=CAST(##SPID AS VARCHAR)
SET #SQL = N'SELECT * INTO ##Tmp1_'+#SPID+' FROM (SELECT min(id) ID,f.ticker,f.ClientCode,f.GroupName,f.RecOrder,' + STUFF((SELECT N',' + #CRLF + N' ' +
N'MAX(CASE FieldName WHEN ' + QUOTENAME(FieldName,'''') + N' THEN FieldValue END) AS ' + QUOTENAME(FieldName)
FROM tblValuationSubGroup g
WHERE ticker=#Ticker AND ClientCode=#ClientCode AND GroupName=#GroupName
GROUP BY FieldName
ORDER BY MIN(FieldOrder)
FOR XML PATH(''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,10,N'') + #CRLF +
N'FROM (select * from tblValuationFieldValue' + #CRLF +
N'WHERE Ticker = '''+#Ticker+''' AND ClientCode = '''+#ClientCode+''' AND GroupName='''+#GroupName+''') f' + #CRLF +
N'GROUP BY f.ticker,f.ClientCode,f.GroupName,f.RecOrder) X';
--EXEC sys.sp_executesql #SQL
EXEC(#SQL)
EXEC('select * from ##Tmp1_'+#SPID+' ORDER BY Broker')
EXEC('DROP TABLE IF EXISTS ##Tmp1_'+#SPID)
George has mentioned why your attempt doesn't work in their answer, so I'm not going to touch on that.
I'm instead going to fix the problem, which I touch on in my comments. In truth, there is no need for a (global) temporary table, you just SELECT ... INTO it and then SELECT from it; you make no further transformations making it pointless. AS such you could just SELECT the data in the first place, no temporary table needed.
I also fix your injection issue; this is a fatal flaw. Dynamic SQL accepts parameters and you using them is a must; not using parameters and instead using injection opens you up to all sorts of errors and security issues.
I can't test this, but I suspect this will work. If not, use your best friend to debug and propagate fixes to the dynamic SQL on any bits I've missed (or provide code we can run above with an MRE).
CREATE Proc USP_GetValuationValue
(
#Ticker VARCHAR(10),
#ClientCode VARCHAR(10),
#GroupName VARCHAR(10)
)
AS
BEGIN
DECLARE #SPID VARCHAR(MAX), --Is this even used now?
#SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SELECT #SPID=CAST(##SPID AS VARCHAR);
SET #SQL = N'SELECT * FROM (SELECT min(id) ID,f.ticker,f.ClientCode,f.GroupName,f.RecOrder,' + STUFF((SELECT N',' + #CRLF + N' ' +
N'MAX(CASE FieldName WHEN ' + QUOTENAME(FieldName,'''') + N' THEN FieldValue END) AS ' + QUOTENAME(FieldName)
FROM tblValuationSubGroup g
WHERE ticker=#Ticker AND ClientCode=#ClientCode AND GroupName=#GroupName
GROUP BY FieldName
ORDER BY MIN(FieldOrder)
FOR XML PATH(''),TYPE).value('(./text())[1]','nvarchar(MAX)'),1,10,N'') + #CRLF +
N'FROM (select * from tblValuationFieldValue' + #CRLF +
N'WHERE Ticker = #Ticker AND ClientCode = #ClientCode AND GroupName= #GroupName) f' + #CRLF +
N'GROUP BY f.ticker,f.ClientCode,f.GroupName,f.RecOrder) X' + #CRLF +
N'ORDER Y Broker;';
--PRINT #SQL; --YOur best friend
--EXEC sys.sp_executesql #SQL --Why did you comment this out? This is correct!
EXEC sys.sp_executesql #SQL, N'#Ticker varchar(10), #ClientCode varchar(10), #GroupName varchar(10)', #Ticker, #ClientCode, #GroupName
END;
I want to change a specific value at unknown table or column. I reached to the code helped me to search it. I found that at some tables, it's the primary column.
I must do this job because I have a lot of values that need to be changed.
I tried to do the code but I got lot of errors:
Invalid column name 'TableNameA'.
Invalid column name 'ColumnNameA'.
Hope get help as I'm still new for SQL.
What should I do?
DECLARE #SearchStrTableName nvarchar(255), #SearchStrColumnName nvarchar(255), #SearchStrColumnValue nvarchar(255), #SearchStrInXML bit, #FullRowResult bit, #FullRowResultRows int
SET #SearchStrColumnValue = '4523'
Declare #NewValueInt int = 4195403
Declare #NewValueVarChar nvarchar(20) = '4194523'
/* use LIKE syntax */
SET #FullRowResult = 1
SET #FullRowResultRows = 3
SET #SearchStrTableName = NULL /* NULL for all tables, uses LIKE syntax */
SET #SearchStrColumnName = NULL /* NULL for all columns, uses LIKE syntax */
SET #SearchStrInXML = 0 /* Searching XML data may be slow */
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
CREATE TABLE #Results (TableName nvarchar(128), ColumnName nvarchar(128), ColumnValue nvarchar(max),ColumnType nvarchar(20))
SET NOCOUNT ON
DECLARE #TableName nvarchar(256) = '',#ColumnName nvarchar(128),#ColumnType nvarchar(20), #QuotedSearchStrColumnValue nvarchar(110), #QuotedSearchStrColumnName nvarchar(110)
SET #QuotedSearchStrColumnValue = QUOTENAME(#SearchStrColumnValue,'''')
DECLARE #ColumnNameTable TABLE (COLUMN_NAME nvarchar(128),DATA_TYPE nvarchar(20))
WHILE #TableName IS NOT NULL
BEGIN
SET #TableName =
(
SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME LIKE COALESCE(#SearchStrTableName,TABLE_NAME)
AND QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > #TableName
AND OBJECTPROPERTY(OBJECT_ID(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)), 'IsMSShipped') = 0
)
IF #TableName IS NOT NULL
BEGIN
DECLARE #sql VARCHAR(MAX)
SET #sql = 'SELECT QUOTENAME(COLUMN_NAME),DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = PARSENAME(''' + #TableName + ''', 2)
AND TABLE_NAME = PARSENAME(''' + #TableName + ''', 1)
AND DATA_TYPE IN (' + CASE WHEN ISNUMERIC(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(#SearchStrColumnValue,'%',''),'_',''),'[',''),']',''),'-','')) = 1 THEN '''tinyint'',''int'',''smallint'',''bigint'',''numeric'',''decimal'',''smallmoney'',''money'',' ELSE '' END + '''char'',''varchar'',''nchar'',''nvarchar'',''timestamp'',''uniqueidentifier''' + CASE #SearchStrInXML WHEN 1 THEN ',''xml''' ELSE '' END + ')
AND COLUMN_NAME LIKE COALESCE(' + CASE WHEN #SearchStrColumnName IS NULL THEN 'NULL' ELSE '''' + #SearchStrColumnName + '''' END + ',COLUMN_NAME)'
INSERT INTO #ColumnNameTable
EXEC (#sql)
WHILE EXISTS (SELECT TOP 1 COLUMN_NAME FROM #ColumnNameTable)
BEGIN
PRINT #ColumnName
SELECT TOP 1 #ColumnName = COLUMN_NAME,#ColumnType = DATA_TYPE FROM #ColumnNameTable
SET #sql = 'SELECT ''' + #TableName + ''',''' + #ColumnName + ''',' + CASE #ColumnType WHEN 'xml' THEN 'LEFT(CAST(' + #ColumnName + ' AS nvarchar(MAX)), 4096),'''
WHEN 'timestamp' THEN 'master.dbo.fn_varbintohexstr('+ #ColumnName + '),'''
ELSE 'LEFT(' + #ColumnName + ', 4096),''' END + #ColumnType + '''
FROM ' + #TableName + ' (NOLOCK) ' +
' WHERE ' + CASE #ColumnType WHEN 'xml' THEN 'CAST(' + #ColumnName + ' AS nvarchar(MAX))'
WHEN 'timestamp' THEN 'master.dbo.fn_varbintohexstr('+ #ColumnName + ')'
ELSE #ColumnName END + ' LIKE ' + #QuotedSearchStrColumnValue
INSERT INTO #Results
EXEC(#sql)
IF ##ROWCOUNT > 0 IF #FullRowResult = 1
BEGIN
SET #sql = 'SELECT TOP ' + CAST(#FullRowResultRows AS VARCHAR(3)) + ' ''' + #TableName + ''' AS [TableFound],''' + #ColumnName + ''' AS [ColumnFound],''FullRow>'' AS [FullRow>],*' +
' FROM ' + #TableName + ' (NOLOCK) ' +
' WHERE ' + CASE #ColumnType WHEN 'xml' THEN 'CAST(' + #ColumnName + ' AS nvarchar(MAX))'
WHEN 'timestamp' THEN 'master.dbo.fn_varbintohexstr('+ #ColumnName + ')'
ELSE #ColumnName END + ' LIKE ' + #QuotedSearchStrColumnValue
EXEC(#sql)
DECLARE #sqlU VARCHAR(MAX)
Declare #sqlN VARCHAR(MAX)
SET #sqlU = N'UPDATE ' + quotename(#SearchStrTableName) + ' SET ' +quotename(#SearchStrColumnName)+ ' = ''' +#NewValueVarChar+ ''' WHERE ' +quotename(#SearchStrColumnName)+ ' = ''' + #SearchStrColumnValue + '''';
EXEC (#sqlU)
SET #sqlN = N'Update ' + quotename(#SearchStrTableName) + ' SET '+ quotename(#SearchStrColumnName) +'=''' +#NewValueVarChar+ '''where'+ quotename(#SearchStrColumnName) + '='+ #SearchStrColumnValue
EXEC (#sqlN)
END
DELETE FROM #ColumnNameTable WHERE COLUMN_NAME = #ColumnName
END
END
END
SET NOCOUNT OFF
IF OBJECT_ID('tempdb..#Abd_tmptbl') IS NOT NULL DROP TABLE #Abd_tmptbl
CREATE TABLE #Abd_tmptbl (TableNameA nvarchar(128), ColumnNameA nvarchar(128), ColumnValueA nvarchar(max),ColumnTypeA nvarchar(20), Count int)
INSERT INTO #Abd_tmptbl
SELECT TableName, ColumnName, ColumnValue, ColumnType, COUNT(*) AS Count FROM #Results
GROUP BY TableName, ColumnName, ColumnValue, ColumnType
DECLARE #Tableee VARCHAR(20), #Columnee varchar(20), #Constraint varchar(20)
DECLARE #items TABLE(tabl int, clmn int)
Create Table #PK_tbl (PK_Col varchar (10))
WHILE EXISTS (select TOP 1 TableNameA FROM #Abd_tmptbl)
BEGIN
SELECT TOP 1 #Tableee = TableNameA, #Columnee = ColumnNameA
------------ GET All PKs of the Table ---------------------
DECLARE #PK_sql varchar(max) = N'INSERT INTO #PK_tbl(PK_Col)
(SELECT Col.Column_Name from INFORMATION_SCHEMA.TABLE_CONSTRAINTS Tab, INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE Col
WHERE Col.Constraint_Name = Tab.Constraint_Name AND Col.Table_Name = Tab.Table_Name AND Constraint_Type = "PRIMARY KEY" AND Col.Table_Name = ' + #Tableee +')'
EXEC (#PK_sql)
------------------------------------------------------------
------------ GET CONTRAINT Name ----------------------------
SET #Constraint = N'SELECT name FROM sys.key_constraints WHERE type = "PK" AND OBJECT_NAME(parent_object_id) = ' + #Columnee
------------------------------------------------------------
------------ RELEASE Table From CONSTRAINTS ----------------
DECLARE #REL_tbl VARCHAR(max) = N'ALTER TABLE' + #Tableee +'DROP CONSTRAINT'+ #Constraint
EXEC (#REL_tbl)
------------------------------------------------------------
DECLARE #Update_tbl VARCHAR(max) = N'UPDATE '+ #Tableee + 'SET ' + #Columnee + '=' + #NewValueInt+ ' WHERE' + #Columnee + '=' + #SearchStrColumnValue
EXEC (#Update_tbl)
DECLARE #Sealing_tbl VARCHAR(max) = N'ALTER TABLE' + #Tableee + 'ADD CONSTRAINT' + #Constraint + 'PRIMARY KEY CLUSTERED (SELECT * FROM #PK_tbl)'
EXEC (#Update_tbl)
END
I have built a stored procedure that aims to identify duplicates in a table and to display the duplicated rows in a meaningful order. It looks like this:
CREATE PROCEDURE [dbo].[spFindDuplicates]
#tableName nvarchar(255),
#field1 nvarchar(255),
#field2 nvarchar(255) = '1',
#field3 nvarchar(255) = '2',
#field4 nvarchar(255) = '3',
#field5 nvarchar(255) = '4'
AS
BEGIN
DECLARE #query AS nvarchar(MAX);
SET #query = '
SELECT *
FROM ' + #tableName + '
WHERE CAST(' + #field1 + ' AS nvarchar(255)) + CAST(' + #field2 + ' AS nvarchar(255)) + CAST(' + #field3 + ' AS nvarchar(255)) + CAST(' + #field4 + ' AS nvarchar(255)) + CAST(' + #field5 + ' AS nvarchar(255))
IN
(
SELECT CAST(' + #field1 + ' AS nvarchar(255)) + CAST(' + #field2 + ' AS nvarchar(255)) + CAST(' + #field3 + ' AS nvarchar(255)) + CAST(' + #field4 + ' AS nvarchar(255)) + CAST(' + #field5 + ' AS nvarchar(255))
FROM ' + #tableName + '
GROUP BY CAST(' + #field1 + ' AS nvarchar(255)) + CAST(' + #field2 + ' AS nvarchar(255)) + CAST(' + #field3 + ' AS nvarchar(255)) + CAST(' + #field4 + ' AS nvarchar(255)) + CAST(' + #field5 + ' AS nvarchar(255))
HAVING COUNT(*) > 1
)
ORDER BY ' + #field1 + ', ' + #field2 + ', ' + #field3 + ', ' + #field4 + ', ' + #field5
EXECUTE(#query);
END
GO
--Example:
EXEC spFindDuplicates #tableName = 'someRandomTable', #field1 = 'firstField', #field2 = 'secondField', #field3 = 'thirdField'
As you can see, I can use at most 5 different fields that I concatenate in order for me to get a key used to determine whether we have a duplicate or not. Please note that I use the CAST function to be able to concatenate fields with various datatypes (varchar, int, dates, etc.).
When I execute the above stored procedure with 5 different fields, it works fine. But I would like to be able to run it with a variable number of fields (from 1 to 5), which is why I provided default values for #field2 to #field5.
But when I execute it with the above example (3 fields provided), I get the following error message:
A column has been specified more than once in the order by list. Columns in the order by list must be unique.
QUESTION: How can I keep ordering the resulting table without getting an error?
BONUS QUESTION: If you find a dynamic way to use that stored procedure with any number of fields (4, 17, or whatever), that'd be even more useful to me.
Like I said in the comments, injection is a huge problem here, and you need to consider it. Saying "Let's consider I don't mind about injection" is naïve and you need to change that attitude. Always make your SQL safe; then there are no excuses and chances for your application being compromised.
As what you are after, I suspect this achieves the goal. There's no need for the subquery to scan your table with an IN here, you can make use of COUNT and the OVER clause within a CTE.
CREATE PROCEDURE [dbo].[FindDuplicates] --I've removed te sp prefix, as sp_ is reserved by MS
#tableName sysname,
#field1 sysname,
#field2 sysname = NULL,
#field3 sysname = NULL,
#field4 sysname = NULL,
#field5 sysname = NULL
AS BEGIN
DECLARE #query AS nvarchar(MAX);
SET #query = N'WITH CTE AS(' + NCHAR(10) +
N' SELECT *' + NCHAR(10) +
N' COUNT(*) OVER (PARTITION BY ' + STUFF(CONCAT(N',' + QUOTENAME(#field1),N',' + QUOTENAME(#field2),N',' + QUOTENAME(#field3),N',' + QUOTENAME(#field4),N',' + QUOTENAME(#field5)),1,1,N'') + N' AS RowCount' + NCHAR(10) +
N' FROM ' + QUOTENAME(#tableName) + N')' + NCHAR(10) +
N'SELECT *' + NCHAR(10) +
N'FROM CTE' + NCHAR(10) +
N'WHERE RowCount > 1' + NCHAR(10) +
N'ORDER BY ' + STUFF(CONCAT(N',' + QUOTENAME(#field1),N',' + QUOTENAME(#field2),N',' + QUOTENAME(#field3),N',' + QUOTENAME(#field4),N',' + QUOTENAME(#field5)),1,1,N'') + N';';
PRINT #query;
--EXEC sys.sp_executesql #query; --Uncomment to rrun the actual query
END
GO
For the command you gave us EXEC dbo.FindDuplicates #tableName = 'someRandomTable', #field1 = 'firstField', #field2 = 'secondField', #field3 = 'thirdField';, this returns the SQL:
WITH CTE AS(
SELECT *
COUNT(*) OVER (PARTITION BY [firstField],[secondField],[thirdField] AS RowCount
FROM [someRandomTable])
SELECT *
FROM CTE
WHERE RowCount > 1
ORDER BY [firstField],[secondField],[thirdField];
Which, I believe gives you the behaviour you are after.
Edited the code to check if the column list exists on the sys.columns there by making sure we get only the columns which are appropriate.
CREATE FUNCTION dbo.fn_SplitString
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
ALTER PROCEDURE [dbo].[spFindDuplicates]
#tableName nvarchar(255),
#columnlist nvarchar(max)
AS
BEGIN
DECLARE #query AS nvarchar(MAX);
SET #columnlist = (SELECT STUFF((SELECT ','+'['+[name]+']'
FROM SYS.columns
WHERE object_id = object_id(#tableName)
AND [Name] IN
(
SELECT Item
FROM dbo.fn_SplitString(#columnlist,',')
)
FOR XML PATH('')
)
,1,1,''))
PRINT #columnlist
SET #query = 'SELECT * FROM (SELECT '+CAST(#columnlist AS NVARCHAR(MAX))+'
FROM '+CAST(#tableName AS nvarchar(MAX))+'
GROUP BY '+CAST(#columnlist AS NVARCHAR(MAX))+'
HAVING COUNT(*) > 1)Res1
ORDER BY '+#columnlist
EXEC SP_EXECUTESQL #query;
END
GO
This is a progression from the question asked here: How to SELECT and UNION from a group of Tables in the schema in SQL Server 2008 R2
I would like to do very much the same thing and the answer given by MarkD works perfectly for the database I am currently working with. Although admittedly I'd like to understand exactly how. How does the query below build the union query from the list of tables returned by the information_schema?
DECLARE #Select_Clause varchar(600) = N'SELECT [Patient_Number] AS [ID number]
,[Attendance Date] AS [Date Seen]
,[Attendance_Type] AS [New/Follow up]
,[Episode Type] AS [Patient Type]
,[Local Authority District]
,Postcode, N''Shaw'' AS Clinic '
,#Where_Clause varchar(100) = N' WHERE [EPISODE TYPE] LIKE N''HIV'''
,#Union_Clause varchar(100) = N' UNION ALL '
,#Query nvarchar(max) = N''
,#RawDataBase varchar(50) = N'BHT_1819_RawData'
,#Schema varchar(50) = N'HIVGUM'
,#Table_Count tinyint;
DECLARE #Table_Count_def nvarchar(100) = N'#TableSchema varchar(50)
,#Table_CountOUT tinyint OUTPUT'
,#Start_Position int = LEN(REPLACE(#Select_Clause, N' ', N'-'))
,#Length int;
SET #Query = N'SELECT #Table_CountOUT = COUNT(*) FROM ' + #RawDataBase +
N'.INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE #TableSchema';
EXEC sp_executesql #query, #Table_Count_def, #TableSchema=#Schema,
#Table_CountOUT=#Table_Count OUTPUT;
SET #Query = N'';
IF #Table_Count > 0
Begin
IF OBJECT_ID(N'dbo.HIV_Cumulative', N'U') is not null
DROP TABLE dbo.HIV_Cumulative;
SELECT #Query = #Query + #Select_Clause + N' FROM ' + #RawDataBase +
N'.HIVGUM.' + TABLE_NAME + #Where_Clause + #Union_Clause
FROM BHT_1819_RawData.INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA LIKE #Schema;
SET #Length = LEN(REPLACE(#query, N' ', N'-')) - #Start_Position -
LEN(REPLACE(#Where_Clause + #Union_Clause, N' ', N'-'));
SELECT #Query = SUBSTRING(#QUERY , #Start_Position+1, #Length)
SET #Query = #Select_Clause + N' INTO BHT_SLR..HIV_Cumulative ' + #QUERY
+ #Where_Clause;
EXEC sp_executesql #Query
End
ELSE
PRINT N'No tables present in database ' + #RawDataBase + N' for Schema ' +
#Schema + N'. You must import source data first.';
The added complication is that I am querying the tables on a separate DB - currently BHT_1819_RawData - so have hard coded the database where it queries the information_schema. What I would really like to do is to specify the separate database using a variable. So that it can be reconfigured to extract from BHT_1920_RawData. I am fairly familiar with exec and sp_executesql, but have only occasionally used output parameters so am not sure what is required here. The attempts that I have made haven't worked. Once I have got this right, I will need to create several other similar scripts that work on the same principle.
Once I realised what needed to happen, I went through some trial and error and came up with a solution:
SET #ParmDef = N'#QueryOut nvarchar(2500) OUTPUT';
SET #sql_string = N'SELECT #QueryOut = #QueryOut + N'''
+ #Select_Clause + ' FROM '
+ #RawDataBase
+ N'.[' + #Schema + N'].'' + TABLE_NAME + N'' '
+ #Where_Clause
+ #Union_Clause
+ N''' FROM '
+ #RawDataBase
+ N'.INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE '''
+ #Schema
+ N''' AND TABLE_NAME NOT LIKE N''%_YTD%''';
EXEC sp_executesql #sql_string, #ParmDef, #QueryOut=#Query OUTPUT;
SET #Length = LEN(REPLACE(#query, N' ', N'-')) - #Start_Position -
LEN(REPLACE(#Where_Clause + #Union_Clause, N' ', N'-'));
SELECT #Query = SUBSTRING(#QUERY , #Start_Position, #Length+1);
SET #Query = REPLACE(#Select_Clause, N'''''', '''') + N' INTO ' + #New_Table + N' ' +
#QUERY + REPLACE(#Where_Clause, N'''''', '''');
EXEC sp_executesql #Query;
When I run the below query, It does print the statements but throws the following error:
Process failed: Incorrect syntax near ')'
Code:
IF EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'Table_1')
DELETE [dbo].[Table_1]
WHERE [UPDATED_DATE] <= DATEADD(dd, -90,'2018.02.23')
UPDATE DBO.T_REF_MASTER
SET LAST_PURGE_DATE = GETDATE()
WHERE TABLE_NAME = 'Table_1'
Can someone explain why is it throwing that error because when I run the individual statements one by one, it executes perfectly
--Query
DECLARE #SQL VARCHAR(MAX)
DECLARE #S VARCHAR(MAX)
DECLARE #SQLExist VARCHAR(MAX)
DECLARE #db VARCHAR(10) = 'dbo'
DECLARE #TABLE_NAME VARCHAR(200) = 'Table_1'
DECLARE #NO_OF_DAYS VARCHAR(10) = '90'
DECLARE #LAST_PURGE_DATE VARCHAR(50) = '2018-02-22 10:46:49.953'
SET #SQLExist = 'IF EXISTS((SELECT 1 FROM
INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '''
+ #db + ''' AND TABLE_NAME = ''' + #TABLE_NAME +
''')'
--PRINT #SQLExist
SET #SQL = 'DELETE [' + #db + '].[' + #TABLE_NAME
+ '] where [UPDATED_DATE] <= DATEADD(dd, -'
+ CONVERT(VARCHAR,#NO_OF_DAYS) + ',''' +
+ CONVERT(VARCHAR,#LAST_PURGE_DATE, 102) + ''')'
--PRINT #SQL
SET #S = 'UPDATE DBO.T_REF_MASTER SET LAST_PURGE_DATE =
getdate() WHERE TABLE_NAME = '
+ '''' + CONVERT(VARCHAR, #TABLE_NAME) + ''');'
PRINT #SQLExist
print #SQL
PRINT #S
I see two opening parentheses - (( - after EXISTS, but only one closing after #TABLE_NAME.
Okay agreed George. I removed those parentheses. Here's the new query:
--Query
DECLARE #SQL VARCHAR(MAX)
DECLARE #S VARCHAR(MAX)
DECLARE #SQLExist VARCHAR(MAX)
DECLARE #db VARCHAR(10) = 'dbo'
DECLARE #TABLE_NAME VARCHAR(200) = 'Table_1'
DECLARE #NO_OF_DAYS VARCHAR(10) = '90'
DECLARE #LAST_PURGE_DATE VARCHAR(50) = '2018-02-22 10:46:49.953'
SET #SQLExist = 'IF EXISTS(SELECT 1 FROM
INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '''
+ #db + ''' AND TABLE_NAME = ''' + #TABLE_NAME +
''')'
--PRINT #SQLExist
SET #SQL = 'DELETE [' + #db + '].[' + #TABLE_NAME
+ '] where [UPDATED_DATE] <= DATEADD(dd, -'
+ CONVERT(VARCHAR,#NO_OF_DAYS) + ',''' +
+ CONVERT(VARCHAR,#LAST_PURGE_DATE, 102) + ''')'
--PRINT #SQL
SET #S = 'UPDATE DBO.T_REF_MASTER SET LAST_PURGE_DATE =
getdate() WHERE TABLE_NAME = '
+ '''' + CONVERT(VARCHAR, #TABLE_NAME) + ''''
PRINT #SQLExist
print #SQL
PRINT #S
Its still spitting out the SYNTAX ERROR NEAR ')'