I'm trying to write a stored procedure to search for a string in all tables of a SQL Server database. I was able to find a good stored procedure for this purpose
However I don't want to just put the #Tablenames manually, I want it to go to a table called Enums_Tables that has an ID, and use that ID as #Tablenames.
What I have being thinking to solve this:
I could write another stored procedure to select all Id's from Enums_Tables and execute the first stored procedure, like in here
I could also pass the parameter in C#, since I'm going to use it as a search textbox. But the ideal would be making a single stored procedure.
Could you please help me with this?
EDIT
Thanks to GPW I have been able to solve this problem. I also encountered problems with the collation, but I also solve it. Below is the final stored procedure.
USE [DynaForms]
GO
/****** Object: stored procedure [dbo].[SP_SearchTables] Script Date: 09/11/2017 14:59:15 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP_SearchTables]
--#Tablenames VARCHAR(500)
#SearchStr NVARCHAR(60)
,#GenerateSQLOnly Bit = 0
,#SchemaNames VARCHAR(500) ='%'
AS
/*
Parameters and usage
#Tablenames -- Provide a single table name or multiple table name with comma seperated.
If left blank , it will check for all the tables in the database
Provide wild card tables names with comma seperated
EX :'%tbl%,Dim%' -- This will search the table having names comtains "tbl" and starts with "Dim"
#SearchStr -- Provide the search string. Use the '%' to coin the search. Also can provide multiple search with comma seperated
EX : X%--- will give data staring with X
%X--- will give data ending with X
%X%--- will give data containig X
%X%,Y%--- will give data containig X or starting with Y
%X%,%,,% -- Use a double comma to search comma in the data
#GenerateSQLOnly -- Provide 1 if you only want to generate the SQL statements without seraching the database.
By default it is 0 and it will search.
##SchemaNames -- Provide a single Schema name or multiple Schema name with comma seperated.
If left blank , it will check for all the tables in the database
Provide wild card Schema names with comma seperated
EX :'%dbo%,Sales%' -- This will search the Schema having names comtains "dbo" and starts with "Sales"
Samples :
1. To search data in a table
EXEC SP_SearchTables #Tablenames = 'T1'
,#SearchStr = '%TEST%'
The above sample searches in table T1 with string containing TEST.
2. To search in a multiple table
EXEC SP_SearchTables #Tablenames = 'T2'
,#SearchStr = '%TEST%'
The above sample searches in tables T1 & T2 with string containing TEST.
3. To search in a all table
EXEC SP_SearchTables #Tablenames = '%'
,#SearchStr = '%TEST%'
The above sample searches in all table with string containing TEST.
4. Generate the SQL for the Select statements
EXEC SP_SearchTables #Tablenames = 'T1'
,#SearchStr = '%TEST%'
,#GenerateSQLOnly = 1
5. To Search in tables with specfic name
EXEC SP_SearchTables #Tablenames = '%T1%'
,#SearchStr = '%TEST%'
,#GenerateSQLOnly = 0
6. To Search in multiple tables with specfic names
EXEC SP_SearchTables #Tablenames = '%T1%,Dim%'
,#SearchStr = '%TEST%'
,#GenerateSQLOnly = 0
7. To specify multiple search strings
EXEC SP_SearchTables #Tablenames = '%T1%,Dim%'
,#SearchStr = '%TEST%,TEST1%,%TEST2'
,#GenerateSQLOnly = 0
8. To search comma itself in the tables use double comma ",,"
EXEC SP_SearchTables #Tablenames = '%T1%,Dim%'
,#SearchStr = '%,,%'
,#GenerateSQLOnly = 0
EXEC SP_SearchTables #Tablenames = '%T1%,Dim%'
,#SearchStr = '%with,,comma%'
,#GenerateSQLOnly = 0
9. To Search by SchemaName
EXEC SP_SearchTables #Tablenames = '%T1%,Dim%'
,#SearchStr = '%,,%'
,#GenerateSQLOnly = 0
,#SchemaNames = '%dbo%,Sales%'
*/
SET NOCOUNT ON
DECLARE #MatchFound BIT
SELECT #MatchFound = 0
DECLARE #CheckTableNames Table
(
Schemaname sysname
,Tablename sysname
)
DECLARE #SearchStringTbl TABLE
(
SearchString VARCHAR(500)
)
DECLARE #SQLTbl TABLE
(
Tablename SYSNAME
,WHEREClause VARCHAR(MAX)
,SQLStatement VARCHAR(MAX)
,Execstatus BIT
)
DECLARE #SQL VARCHAR(MAX)
DECLARE #TableParamSQL VARCHAR(MAX)
DECLARE #SchemaParamSQL VARCHAR(MAX)
DECLARE #TblSQL VARCHAR(MAX)
DECLARE #tmpTblname sysname
DECLARE #ErrMsg VARCHAR(100)
/*
IF LTRIM(RTRIM(#Tablenames)) IN ('' ,'%')
BEGIN
INSERT INTO #CheckTableNames
SELECT Name
FROM sys.tables
END
ELSE
BEGIN
IF CHARINDEX(',',#Tablenames) > 0
SELECT #SQL = 'SELECT ''' + REPLACE(#Tablenames,',','''as TblName UNION SELECT ''') + ''''
ELSE
SELECT #SQL = 'SELECT ''' + #Tablenames + ''' as TblName '
SELECT #TblSQL = 'SELECT T.NAME
FROM SYS.TABLES T
JOIN (' + #SQL + ') tblsrc
ON T.name LIKE tblsrc.tblname '
INSERT INTO #CheckTableNames
EXEC(#TblSQL)
END
*/
--IF LTRIM(RTRIM(#Tablenames)) = ''
--BEGIN
-- SELECT #Tablenames = '%'
--END
IF LTRIM(RTRIM(#SchemaNames)) =''
BEGIN
SELECT #SchemaNames = '%'
END
--IF CHARINDEX(',',#Tablenames) > 0
-- SELECT #TableParamSQL = 'SELECT ''' + REPLACE(#Tablenames,',','''as TblName UNION SELECT ''') + ''''
--ELSE
-- SELECT #TableParamSQL = 'SELECT ''' + #Tablenames + ''' as TblName '
IF CHARINDEX(',',#SchemaNames) > 0
SELECT #SchemaParamSQL = 'SELECT ''' + REPLACE(#SchemaNames,',','''as SchemaName UNION SELECT ''') + ''''
ELSE
SELECT #SchemaParamSQL = 'SELECT ''' + #SchemaNames + ''' as SchemaName '
SELECT #TblSQL = 'SELECT SCh.NAME,T.NAME
FROM SYS.TABLES T
JOIN SYS.SCHEMAS SCh
ON SCh.SCHEMA_ID = T.SCHEMA_ID
INNER JOIN [DynaForms].[dbo].[Enums_Tables] et on
(et.Id = T.NAME COLLATE Latin1_General_CI_AS)'
INSERT INTO #CheckTableNames
(Schemaname,Tablename)
EXEC(#TblSQL)
IF NOT EXISTS(SELECT 1 FROM #CheckTableNames)
BEGIN
SELECT #ErrMsg = 'No tables are found in this database ' + DB_NAME() + ' for the specified filter'
PRINT #ErrMsg
RETURN
END
IF LTRIM(RTRIM(#SearchStr)) =''
BEGIN
SELECT #ErrMsg = 'Please specify the search string in #SearchStr Parameter'
PRINT #ErrMsg
RETURN
END
ELSE
BEGIN
SELECT #SearchStr = REPLACE(#SearchStr,',,,',',#DOUBLECOMMA#')
SELECT #SearchStr = REPLACE(#SearchStr,',,','#DOUBLECOMMA#')
SELECT #SearchStr = REPLACE(#SearchStr,'''','''''')
SELECT #SQL = 'SELECT ''' + REPLACE(#SearchStr,',','''as SearchString UNION SELECT ''') + ''''
INSERT INTO #SearchStringTbl
(SearchString)
EXEC(#SQL)
UPDATE #SearchStringTbl
SET SearchString = REPLACE(SearchString ,'#DOUBLECOMMA#',',')
END
INSERT INTO #SQLTbl
( Tablename,WHEREClause)
SELECT QUOTENAME(SCh.name) + '.' + QUOTENAME(ST.NAME),
(
SELECT '[' + SC.Name + ']' + ' LIKE ''' + REPLACE(SearchSTR.SearchString,'''','''''') + ''' OR ' + CHAR(10)
FROM SYS.columns SC
JOIN SYS.types STy
ON STy.system_type_id = SC.system_type_id
AND STy.user_type_id =SC.user_type_id
CROSS JOIN #SearchStringTbl SearchSTR
WHERE STY.name in ('varchar','char','nvarchar','nchar','text')
AND SC.object_id = ST.object_id
ORDER BY SC.name
FOR XML PATH('')
)
FROM SYS.tables ST
JOIN #CheckTableNames chktbls
ON chktbls.Tablename = ST.name
JOIN SYS.schemas SCh
ON ST.schema_id = SCh.schema_id
AND Sch.name = chktbls.Schemaname
WHERE ST.name <> 'SearchTMP'
GROUP BY ST.object_id, QUOTENAME(SCh.name) + '.' + QUOTENAME(ST.NAME) ;
UPDATE #SQLTbl
SET SQLStatement = 'SELECT * INTO SearchTMP FROM ' + Tablename + ' WHERE ' + substring(WHEREClause,1,len(WHEREClause)-5)
DELETE FROM #SQLTbl
WHERE WHEREClause IS NULL
WHILE EXISTS (SELECT 1 FROM #SQLTbl WHERE ISNULL(Execstatus ,0) = 0)
BEGIN
SELECT TOP 1 #tmpTblname = Tablename , #SQL = SQLStatement
FROM #SQLTbl
WHERE ISNULL(Execstatus ,0) = 0
IF #GenerateSQLOnly = 0
BEGIN
IF OBJECT_ID('SearchTMP','U') IS NOT NULL
DROP TABLE SearchTMP
EXEC (#SQL)
IF EXISTS(SELECT 1 FROM SearchTMP)
BEGIN
SELECT Tablename=#tmpTblname,* FROM SearchTMP
SELECT #MatchFound = 1
END
END
ELSE
BEGIN
PRINT REPLICATE('-',100)
PRINT #tmpTblname
PRINT REPLICATE('-',100)
PRINT replace(#SQL,'INTO SearchTMP','')
END
UPDATE #SQLTbl
SET Execstatus = 1
WHERE Tablename = #tmpTblname
END
IF #MatchFound = 0
BEGIN
SELECT #ErrMsg = 'No Matches are found in this database ' + DB_NAME() + ' for the specified filter'
PRINT #ErrMsg
RETURN
END
SET NOCOUNT OFF
Sorry in advance with the weird formatting, I can't put it more readable. I hope it helps someone. I also want to give credit to the onwer of the original stored procedure in here.
That stored procedure you linked to already has logic to check the table names in the database and populates a table variable with a list of tables to check. Just change this logic to Select from your ENUM_TABLES table instead. This could be based on an input parameter of #Id if you like...
In simple terms:
Remove the parameter #TableNames from the stored procedure
(optionally) replace the parameter with #ENUM_TABLE_ID or something
Change code in SP that looks like this:
IF LTRIM(RTRIM(#Tablenames)) = ''
/* Removed a load of lines looking at #TableNames...... */
....
SELECT #TblSQL = 'SELECT SCh.NAME,T.NAME
FROM SYS.TABLES T
JOIN SYS.SCHEMAS SCh
ON SCh.SCHEMA_ID = T.SCHEMA_ID
JOIN (' + #TableParamSQL + ') tblsrc
ON T.name LIKE tblsrc.tblname
JOIN (' + #SchemaParamSQL + ') schemasrc
ON SCh.name LIKE schemasrc.SchemaName
With something more like this:
SELECT #TblSQL = 'SELECT SCh.NAME,T.NAME
FROM SYS.TABLES T
JOIN SYS.SCHEMAS SCh
ON SCh.SCHEMA_ID = T.SCHEMA_ID
INNER JOIN ENUM_TABLES et on
(et.TABLENAME=T.NAME)
and (et.Id='+#ENUM_TABLE_ID+')'
And then I think it'll do what you want (lookup the list of tables from another table, based on an ID passed into the stored procedure)
(apologies for the slightly weird formatting of the SQL above; for some reason the SO markdown processor really didn't like that stuff, but it is hopefully readable. If anyone wants to try to edit this to improve it, be my guest.)
This question already has answers here:
How to use sp_executesql to avoid SQL Injection
(2 answers)
Closed 5 years ago.
My table data is like this:
enter image description here
I'm using this query:
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT #cols = STUFF((SELECT distinct ','+ QUOTENAME('COMPLETE_' + cast(row_number() over(partition by CID order by CID) as varchar(10)))
FROM allleads
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = 'SELECT CustomerName, address, CID, ' + #cols +
' FROM (SELECT CustomerName, address, CID, COMPLETE,''COMPLETE_''+ CAST(row_number() over (partition by CID order by RecordDate) as varchar(10)) val from allleads) x PIVOT (MAX(COMPLETE) for val in (' + #cols + ')) p ' execute(#query)
is working fine.
But when I add a search condition between date like this:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#dt AS VARCHAR(10),
#dt1 AS VARCHAR(10) set
#dt='2017/05/18' set
#dt1='2017/07/10'
select #cols = STUFF((SELECT distinct ','+
QUOTENAME('COMPLETE_' +
cast(row_number()
over(partition by CID order by CID) as varchar(10)))
from allleads FOR XML PATH(''),
TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #query = 'SELECT CustomerName,address,CID, ' +
#cols + ' from (select CustomerName,address,CID, COMPLETE,''COMPLETE_''+
cast(row_number()
over(partition by CID order by RecordDate) as varchar(10))
val from allleads **where convert(varchar(10),RecordDate,111) between '+#dt+' and '+#dt1+'**) x pivot(max(COMPLETE) for val in (' + #cols + ')) p ' execute(#query)
then it is showing an error:
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value '2017/05/19' to data type int.
Please help.
Thanks in advance.
You should really use parameters for constants in a query . . . and use sp_executesql to pass them in.
In your case, the problem is missing quotes around the dates. Instead of:
between '+#dt+' and '+#dt1+'
You can do:
between '''+#dt+''' and '''+#dt1+'''
In other words, the error is occurring when you execute the code, not when you are defining the strings.
You should also learn to write your code in a less sloppy manner. I don't see how even you can read it, much less anyone else.
I am trying to show the results of a dynamic pivot in a c# datagridview. So far I have got the following code but I am stumped as how to incorporate the #Date variable in the #query string. What am I missing here? The code works fine with hard coded dates and as it is returns Additional information: Incorrect syntax near '#Date'. Please help,
Thanks,
A
da2.SelectCommand = new SqlCommand(#"DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(currency)
FROM Alpha.dbo.Beta
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query = 'SELECT Customer, ' + #cols + ' FROM
(
SELECT
Customer, Amount, Currency
FROM Alpha.dbo.Beta
WHERE Date Between ''2010-01-01'' and '#Date' ----PROBLEM AREA----
) x
PIVOT
(
SUM(Amount)
for Currency in (' + #cols + ')
) AS pvt
ORDER BY Customer; '
execute(#query)", MyConnection);
da2.SelectCommand.Parameters.Add("Date", SqlDbType.DateTime).Value = dateTimePicker4.Text;
ds2.Clear();
da2.Fill(ds2);
Correct your code as:
da2.SelectCommand.Parameters.Add("#Date", SqlDbType.DateTime);
da2.SelectCommand.Parameters["#Date"].Value = dateTimePicker4.Text;
Also, use stored procedure instead the query.
I want to PassColumn name as a paremeter in My SP
And if my tat column exists in first table (Batch_Master), want to fatch value from that column,
And if that column exists in my second table (GTIN_Master), want to fetch value from tat table column,
Each table have columns like..
Batch_Master (Batch_M_id, GTIN(primary key),....etc
GTIN_Master (GTIN (foreign key),..etc)
I have Batch_M_id, and column name as a parameter..
Note: column Name having random datatype, some time int or some time datetime etc
I Try followin SP
CREATE PROCEDURE dbo.StoredProcedure2
#columnName varchar(50),
#batchmId int
AS
if exists(select * from sys.columns
where Name = N'columnName' and Object_ID = Object_ID(NBatch_Master'))
begin
select #columnName from Batch_Master
end
else
begin
select #columnName
from GTIN_Master inner join Batch_Master
on GTIN_Master.GTIN = Batch_Master.GTIN
where Batch_M_id =#batchmId
end
RETURN
What you need when you do not know exactly the query structure you are going to execute, then you have to create a dynamic query, which is actually a form of templating queries.
CREATE PROCEDURE dbo.StoredProcedure2
#columnName varchar(50),
#batchmId int
AS
DECLARE #SQL1 AS VARCHAR(MAX)
DECLARE #SQL2 AS VARCHAR(MAX)
SET #SQL1 = 'select ' + #columnName + ' from Batch_Master'
SET #SQL1 = 'select ' + #columnName + '
from GTIN_Master inner join Batch_Master
on GTIN_Master.GTIN = Batch_Master.GTIN
where Batch_M_id =' + CONVERT(VARCHAR,#batchmId)
IF EXISTS(SELECT * FROM sys.columns WHERE Name = #columnName and Object_ID = Object_ID(N'Batch_Master'))
BEGIN
EXEC (#SQL1)
END
ELSE
BEGIN
EXEC (#SQL2)
END
The above will do what you want but is prone to errors. For instance what if the column passed in the parameter does not exist in the tables used in the second query? You should probably need to check for existence in the second case too.
DECLARE #SQL VARCHAR(MAX)
SET #SQL = 'select ' + #columnName + ' from Batch_Master'
EXECUTE(#SQL)
This SQL:
set nocount on
go
create table One (id int, name varchar(25))
go
create table Two (id int, value varchar(25))
go
insert into One values (1, 'Table One')
insert into Two values (1, 'Table Two')
go
create procedure sp_test_colname
#colname sysname
as
declare #sql nvarchar(2048)
if exists (select 1 from sys.columns where name = #colname and object_id = object_id(N'One'))
set #sql = 'select [' + #colname + '] from [One]'
else
if exists (select 1 from sys.columns where name = #colname and object_id = object_id(N'Two'))
set #sql = 'select [' + #colname + '] from [Two]'
if #sql <> ''
exec sp_executesql #sql
go
exec sp_test_colname 'name'
exec sp_test_colname 'value'
exec sp_test_colname 'somethingelse'
go
drop procedure sp_test_colname
drop table One, Two
Returns the following:
name
-------------------------
Table One
value
-------------------------
Table Two
USE [SAFLA]
DECLARE
#cols AS NVARCHAR(MAX)
, #query AS NVARCHAR(MAX)
SET #cols = STUFF((
SELECT DISTINCT ',' + QUOTENAME(w.week_range)
FROM Weekly_Attendance AS w
FOR XML PATH (''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET #query = '
SELECT stu_id, name, ' + #cols + '
FROM (
SELECT
stu.stu_id
, stu.name
, w.total_present_lec
, w.week_range
FROM Student_Registration AS stu
JOIN Weekly_Attendance w ON stu.stu_id = w.stu_id
) x
PIVOT
(
SUM(total_present_lec)
FOR week_range in (' + #cols + ')
--GROUP BY total_present_lec
) p'
EXECUTE (#query)
This is a sql query i want integrate with ASP.NET Page..