Multiple search filter using stored procedure - sql

I already have a stored procedure for the search filter but it's complex and long, how do enhance the stored procedure code?
I have 3 search filters: group, key and label, these search filters are related to one another.
My stored procedure code:
IF (#group <> '' AND #key <> '' AND #label <> '')
BEGIN
SET #statement =
#statement + ' WHERE ([group] LIKE ''%' + #group + '%'' AND [key] LIKE ''%' + #key + '%'' AND [label] LIKE ''%' + #label + '%'')'
END
ELSE IF (#group <> '' AND #key <> '')
BEGIN
SET #statement =
#statement + ' WHERE ([group] LIKE ''%' + #group + '%'' AND [key] LIKE ''%' + #key + '%'')'
END
ELSE IF (#key <> '' AND #label <> '')
BEGIN
SET #statement =
#statement + ' WHERE ([key] LIKE ''%' + #key + '%'' AND [label] LIKE ''%' + #label + '%'')'
END
ELSE IF (#label <> '' AND #group <> '')
BEGIN
SET #statement =
#statement + ' WHERE ([label] LIKE ''%' + #label + '%'' AND [group] LIKE ''%' + #group + '%'')'
END
ELSE IF (#group <> '')
BEGIN
SET #statement
= #statement + ' WHERE [group] LIKE ''%' + #group + '%'''
END
ELSE IF (#key <> '')
BEGIN
SET #statement
= #statement + ' WHERE [key] LIKE ''%' + #key + '%'' '
END
ELSE IF (#label <> '')
BEGIN
SET #statement
= #statement + ' WHERE [label] LIKE ''%' + #label + '%'''
END
How do I modify the code to be simpler?

SET #statement =
#statement + ' WHERE
( (#group<>'' and [group] LIKE ''%' + #group + '%''') or (#group='' and 1=1))
( (#key <>'' and [key] LIKE ''%' + #key + '%''') or (#key ='' and 1=1))
( (#label <>'' and [label] LIKE ''%' + #label + '%''') or (#label ='' and 1=1))
Please try using above way using sql injection

I have used the following pattern in the past when creating stored procedures with input arguments that represent multiple optional search criteria.
The advantage of this technique are
No dynamic SQL
The query is clean
The WHERE clause has multiple ANDs (no ORs) allowing SQL to use any indexes you may have on the search columns
This is only an example but hopefully you get the idea and it's something you can build on.
-- Procedure Input Arguments
DECLARE
#IntExample INT = NULL
,#StringExample VARCHAR(50) = NULL;
-- Constants
DECLARE #MinInt32 INT = (-2147483648), #MaxInt32 INT = 2147483647;
-- Input Argument Validation
DECLARE #IdMin INT = #MinInt32, #IdMax INT = #MaxInt32;
IF (#IntExample IS NOT NULL)
BEGIN
SET #IdMin = #IntExample;
SET #IdMax = #IntExample;
END
DECLARE #DescFilter NVARCHAR(50) = '%';
IF (#StringExample IS NOT NULL)
BEGIN
SET #DescFilter = '%' + #StringExample + '%';
END
-- Procedure Query
SELECT *
FROM dbo.MyTable
WHERE
(
Id BETWEEN #IdMin AND #IdMax
AND [Description] LIKE #DescFilter
);

Assuming that you're only building dynamic SQL for the WHERE clause's sake.
This one will probably perform best due to short circuiting
SELECT *
FROM dbo.MyTable
WHERE
((#group = '') OR ([group] LIKE '%' + #group + '%'))
AND ((#key = '') OR ([key] LIKE '%' + #key + '%'))
AND ((#label = '') OR ([label] LIKE '%' + #label + '%'))
This will also work, but it will be heavier on resources and not as readable as the one above.
SELECT *
FROM dbo.MyTable
WHERE
([group] LIKE STUFF('%%', 2, 0, #group) )
AND ([key] LIKE STUFF('%%', 2, 0, #key) )
AND ([label] LIKE STUFF('%%', 2, 0, #label) )

Related

SQL : Select a table name containing specific string [duplicate]

This question already has answers here:
Find a value anywhere in a database
(18 answers)
Search all tables, all columns for a specific value SQL Server [duplicate]
(4 answers)
Closed 5 years ago.
Suppose I have string to search "Sample String" and I have 100 tables in my database.
I want to search if any of the table contains this string.
Eg. If tlbSample contains string "Sample String" then it should show it.
I tried :-
SELECT name
FROM sys.tables
WHERE name LIKE '%Sample String%'
But no luck.
Please help me.
You can use this. I have copied it from here : https://stackoverflow.com/a/13588431/400447
DECLARE #SearchStrTableName nvarchar(255), #SearchStrColumnName nvarchar(255), #SearchStrColumnValue nvarchar(255), #SearchStrInXML bit, #FullRowResult bit, #FullRowResultRows int
SET #SearchStrColumnValue = '%searchthis%' /* 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)
END
DELETE FROM #ColumnNameTable WHERE COLUMN_NAME = #ColumnName
END
END
END
SET NOCOUNT OFF
SELECT TableName, ColumnName, ColumnValue, ColumnType, COUNT(*) AS Count FROM #Results
GROUP BY TableName, ColumnName, ColumnValue, ColumnType

Dynamic vs Static SQL with multiple conditions

The "Where" clause of my SQL will vary depending on the values provided. I wrote 2 versions (Dynamic and static). I read that Dynamic is the way to go if we have multiple parameters with a variable where clause. Also, this is just a sample, my real SQL has many more conditions, but something along these lines. Which among the two is the right choice for this scenario?
Dynamic SQL:
DECLARE #SQL NVARCHAR(max)='SELECT ID,Name,sex,street,city,state,zip,phone FROM Test';
DECLARE #whereSql VARCHAR(2000) = 'WHERE city= #City';
DECLARE #OrderBy VARCHAR(2000) = 'order by name';
IF(#Name IS NOT NULL)
BEGIN
SET #Name = #Name + '%'
SET #whereSql = #whereSql + ' ' + 'AND name like #Name';
end
IF(#Gender IS NOT NULL)
BEGIN
SET #whereSql = #whereSql + ' ' + 'AND sex =' + '#Gender';
END
IF(#Address IS NOT NULL)
BEGIN
SET #Address = '%' + #Address + '%'
SET #whereSql = #whereSql + ' ' + 'AND street like ' + '#Address';
END
SET #SQL = #SQL +' ' + #whereSql + ' '+ #OrderBy;
EXEC sp_executesql #SQL, N'#Gender VARCHAR(10), #Name VARCHAR(200), #Address VARCHAR(250)', #Gender,#Name,#Address;
Static SQL:
SELECT ID,Name,sex,street,city,state,zip,phone FROM Test T1 WHERE
(#Name IS NULL OR (T1.Name LIKE +'%'+ #Name + '%'))
AND
(#Gender is null OR (T1.sex = #Gender))
AND
(#Address is null or (T1.street LIKE +'%'+ #Address + '%'))

SQL with AND statements, dynamically building up

Aim: I'm building a dynamic SQL string and want to make the search an AND function
Code type: SQL stored procedure within SQL Server Management Studio
Issue: If the first search is not required then I need to know this (I know because the default is '0' in this case. I feel I'm missing a sitter but don't seem to be able to stackoverflow/Google for the solution.
I set up #QueryString with a default of '' so the functionality will work.
What will fix this?:
I've thought about COALESCE and potential use of IF ELSE within the IF but I am hoping there is clean solution along the lines of
SET #QUERYSTRING = IF(#QUERYSTRING = '','', + + ' FIELD1 LIKE ''%' + LTRIM(RTRIM(#s1)) + '%' )
Current example (snippet):
ALTER PROCEDURE [dbo].[spGridSearchTest]
#s1 NVARCHAR(20),
#s2 VARCHAR(20)
AS
BEGIN
DECLARE #QUERY NVARCHAR(MAX) = ''
DECLARE #QUERYSTRING NVARCHAR(MAX) = ''
SET #QUERY = 'SELECT * FROM TblTable'
IF #s1 <> '1234xyz'
SET #QUERYSTRING = #QUERYSTRING + ' Field1 LIKE ''%' + LTRIM(RTRIM(#s1)) + '%'
IF #s2 <> '1234xyz'
SET #QUERYSTRING = #QUERYSTRING + ' Field2 LIKE ''%' + LTRIM(RTRIM#s2)) + '%'
IF LEN(LTRIM(RTRIM(#QUERYSTRING))) > 0
SET #QUERY = LTRIM(RTRIM(#QUERY)) + ' WHERE ' + LTRIM(RTRIM(#QUERYSTRING)) + ''''
EXECUTE(#QUERY)
END
If I understand better your issue:
Try this:
ALTER PROCEDURE [dbo].[spGridSearchTest]
#s1 NVARCHAR(20),
#s2 VARCHAR(20)
AS
BEGIN
DECLARE #QUERY NVARCHAR(MAX) = ''
DECLARE #QUERYSTRING NVARCHAR(MAX) = ''
DECLARE #conditionadded char(1) = 'N'
SET #QUERY = 'SELECT * FROM TblTable'
IF #s1 <> '1234xyz'
BEGIN
SET #QUERYSTRING = ' Field1 LIKE ''%' + LTRIM(RTRIM(#s1)) + '%'
SET #conditionadded = 'Y'
END
IF #s2 <> '1234xyz'
BEGIN
IF (#conditionadded = 'Y')
BEGIN
SET #QUERYSTRING = #QUERYSTRING + ' AND '
END
SET #QUERYSTRING = #QUERYSTRING + ' Field2 LIKE ''%' + LTRIM(RTRIM#s2)) + '%'
SET #conditionadded = 'Y'
END
IF (#conditionadded = 'Y')
BEGIN
SET #QUERY = LTRIM(RTRIM(#QUERY)) + ' WHERE ' + LTRIM(RTRIM(#QUERYSTRING)) + ''''
END
EXECUTE(#QUERY)
END
Do you really need a dynamic query? Why not use something like this:
select
Whatever
from MyTable
where
(#s1 = '1234xyz' or Field1 = #s1)
and
(#s2 = '1234xyz' or Field2 = #s2)
This avoids a security hole, and depending on your query patterns and data set, it might even be faster. And of course, it's pretty easy to read, and you don't have to deal with SQL in strings :)

Population of Visual Studio Database Project data-loading scripts from existing data

So, I've been hunting for a solution for this for awhile and have come up with what sort of works ... but I can't help feeling that there must be something more elegant.
What I'm looking for is to be able to extract existing data from a populated database & incorporate that data into loading scripts. The database schema & configuration data will be rolled out multiple times, and will change as development continues, so it's important to be able to rebuild the configuration data from existing data, rather than from static data kept in scripts.
Here's what I've cobbled together:
create procedure #dump (
#TableName varchar(128)
)
as
set nocount on
set rowcount 0
declare #template varchar(max)
set #template = 'SET IDENTITY_INSERT [dbo].[' + #TableName + '] ON
MERGE INTO [dbo].[' + #TableName + '] AS [Target]
USING
(
VALUES
{vals}
)
AS [Source] ({fields})
ON [Target].[{pk}] = [Source].[{pk}]
WHEN MATCHED THEN UPDATE SET
{upds}
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
{fields}
)
VALUES
(
{fields}
);
SET IDENTITY_INSERT [dbo].[' + #TableName + '] OFF
--------------------------------------------------
'
declare #pk varchar(max) = ''
declare #vals varchar(max) = '/*
set concat_null_yields_null off
select ''' + '(' + ''' + replace(replace({casts} + '') ,'', '',,'', '', null,''), ''' + ',)' + ''', ''' + ',null)' + ''') from [' + #TableName + ']
*/'
declare #casts varchar(max) = ''
declare #fields varchar(max) = ''
declare #upds varchar(max) = ''
declare #inserts varchar(max) = ''
set #pk = SUBSTRING(#TableName, 1, len(#TableName) - 1) + 'ID'
declare cur_flds
cursor for select c.name, c.type
from syscolumns c
where c.id = object_id(#TableName)
order by c.colid
declare #fn varchar(max)
declare #ft int
open cur_flds
fetch next from cur_flds into #fn, #ft
while ##FETCH_STATUS = 0
begin
if len(#fields) > 0
set #fields = #fields + ', '
set #fields = #fields + '[' + #fn + ']'
if len(#casts) > 0
set #casts = #casts + ' + ' + ''','' + '
if #ft in(56,55,50,38,48)
set #casts = #casts + 'cast([' + #fn + '] as varchar)'
else if #ft = 111
set #casts = #casts + ''''''''' + ' + 'cast([' + #fn + '] as varchar) + ' + ''''''''''
else
set #casts = #casts + ''''''''' + ' + 'replace([' + #fn + '], ''''''''' + ', ' + ''''''''''''') + '''''''''
if #fn != #pk
begin
if len(#upds) > 0
set #upds = #upds + ', '
set #upds = #upds + '[Target].[' + #fn + '] = [Source].[' + #fn + ']'
end
fetch next from cur_flds into #fn, #ft
end
close cur_flds
deallocate cur_flds
set #vals = REPLACE(#vals, '{casts}', #casts)
set #template = REPLACE(#template, '{pk}', #pk)
set #template = REPLACE(#template, '{vals}', #vals)
set #template = REPLACE(#template, '{fields}', #fields)
set #template = REPLACE(#template, '{upds}', #upds)
set #template = REPLACE(#template, '{inserts}', #inserts)
print #template
go
exec #dump 'ActionItemSystems'
drop proc #dump
That ends up giving me output of:
SET IDENTITY_INSERT [dbo].[ActionItemSystems] ON
MERGE INTO [dbo].[ActionItemSystems] AS [Target]
USING
(
VALUES
/*
set concat_null_yields_null off
select '(' + replace(replace(cast([ActionItemSystemID] as varchar) + ',' + '''' + replace([ActionItemSystemName], '''', '''''') + '''' + ') ,', ',,', ', null,'), ',)', ',null)') from [ActionItemSystems]
*/
)
AS [Source] ([ActionItemSystemID], [ActionItemSystemName])
ON [Target].[ActionItemSystemID] = [Source].[ActionItemSystemID]
WHEN MATCHED THEN UPDATE SET
[Target].[ActionItemSystemName] = [Source].[ActionItemSystemName]
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
[ActionItemSystemID], [ActionItemSystemName]
)
VALUES
(
[ActionItemSystemID], [ActionItemSystemName]
);
SET IDENTITY_INSERT [dbo].[ActionItemSystems] OFF
From this point, I can take the commented-out bit
set concat_null_yields_null off
select '(' + replace(replace(cast([ActionItemSystemID] as varchar) + ',' + '''' + replace([ActionItemSystemName], '''', '''''') + '''' + ') ,', ',,', ', null,'), ',)', ',null)') from [ActionItemSystems]
execute that, and get output like:
(33,'7111-5 -Upstream/Seed Lab') ,
(32,'7301-Seed Lab') ,
(30,'7807 UFDF') ,
(14,'BAS Panel Upgrade') ,
(1,'Clean Steam') ,
(13,'DCS') ,
(2,'HWFI') ,
(3,'MCS') ,
(12,'MES') ,
(31,'Seed Lab') ,
(18,'UCS WRO') ,
(34,'Upstream Seed Lab') ,
(29,'Viral Filtration') ,
which can then be incorporated (sans the final comma) into the script.
Now, this solution functions, but it's fragile. It depends on various assumptions (e.g., that the Table Name will have a Primary Key of Table Name - trailing 's' and plus ID) that may not hold true for every solution. It also requires cutting & pasting, and rerunning from the start when the table structures change.
This is probably quite a lot of background ... which I'm partly sharing because I couldn't find anything similar out there & thought that somebody might benefit from this. However, I still come back to my real question, which is to say: where's the tool to generate this kind of script for VS Database Projects? There really should be something - something that would take into account whatever the primary key is, that would generate the thing entire, etc.
You can try with this procedure for generating MERGE statements:
https://github.com/readyroll/generate-sql-merge
It is more advanced version of what you already have.

Correct escaping for Where clause in dynamic procedure

I am pretty new to dynamic procedure and am trying to use something like the below in a Where clause there.
I have covered the rest of the procedure but have an issue with the below.
What would the correct escaping (quotes) look like in this case to cover the variable inputs? Also, I am not sure how to handle the part "R.#searchCategory" as here I need to add the "R.".
SQL Query :
WHERE ' + #selection + ' = ''closed'' AND (R.logStatus LIKE ''%Completed%'' OR R.logStatus LIKE ''%Closed%'')
AND
(
(
' + #searchCategory + ' <> ''dateRec'' AND R.' + #searchCategory + ' LIKE ''%' + #searchTerm + '%''
)
OR
(
' + #searchCategory + ' = ''dateRec'' AND CAST(R.dateRec AS DATE) LIKE ''%' + #searchTerm + '%''
)
)
Make your life easier and use REPLACE and QUOTENAME
SET #sql = ...
'WHERE #selection = ''closed'' ' +
' AND (R.logStatus LIKE ''%Completed%'' ' +
' OR R.logStatus LIKE ''%Closed%'') ' +
' AND ( ' +
' (#searchCategoryText <> ''dateRec'' ' +
' AND R.#searchCategoryColumn LIKE ''%'' + #searchTerm + ''%'' ' +
' ) ' +
' OR ' +
' (#searchCategoryText = ''dateRec'' ' +
' AND CAST(R.dateRec AS DATE) LIKE ''%'' + #searchTerm + ''%'' ' +
' ) ' +
' ) ';
SET #sql = REPLACE(#sql, '#selection', QUOTENAME(#selection, ''''));
SET #sql = REPLACE(#sql, '#searchCategoryText', QUOTENAME(#searchcategory, ''''));
SET #sql = REPLACE(#sql, '#searchCategoryColumn', QUOTENAME(#searchcategory));
SET #sql = REPLACE(#sql, '#searchTerm', QUOTENAME(#searchTerm, ''''));
you have to separate out each whenever you add your variable like
WHERE ' + #selection +'= ''closed'' AND (R.logStatus LIKE ''%Completed%'' OR R.logStatus LIKE ''%Closed%'')
AND
(
(
' + #searchCategory +' <> ''dateRec'' AND R.' + #selection +'#searchCategory LIKE ''%' + ' + #searchTerm+'%''
)
OR
(
' + #searchCategory +'= ''dateRec'' AND CAST(R.dateRec AS DATE) = ' + #searchTerm +'
)
)
Still you got error then make direct query on which you make dynamic latter. i.e.
declare #searchCategory nvarchar(100) = 'abc'
select * from table where colname = #searchCategory
Run this and check the result properly come, now you just add into string here and your query ready to execute , simple.