SQL like for comma separated input - sql

I want to write a stored procedure in which I want to run a query for multiple input which comes as a comma separated string. Just like we have in for exact match, can I have something like in too?
Input:
51094,51096,512584
Attempting to do:
select * from table where column like ('%51094%','%51096%','%512584%')
My query should iterate through each input and get the column which matches the pattern.
I have already tried following:
Contains(Column, '"*51094*" or "*51096*" or "*512584*")
But can't configure freetext search now.
Source: Is there a combination of "LIKE" and "IN" in SQL?
All the proposed types in: How to use SQL LIKE condition with multiple values in PostgreSQL?
None seems to be working.
Please suggest a simple way.

Try with first explode your input
$arr = explode($Input,",");
column like "%".$arr[0]."%" OR
column like "%".$arr[1]."%" OR
column like "%".$arr[2]."%"

This function you can use, no any mandatory to give comma only you can give special character.
ALTER function [dbo].[SplitString] (#String nvarchar(4000), #Delimiter char(1))
Returns #Results Table (Items nvarchar(50))
As
Begin
Declare #Index int
Declare #name nvarchar(20)
Declare #Slice nvarchar(50)
Select #Index = 1
If #String Is NULL Return
While #Index != 0
Begin
Select #Index = CharIndex(#Delimiter, #String)
If #Index <> 0
Select #Slice = left(#String, #Index - 1)
else
Select #Slice = #String
Insert into #Results(Items) Values (#Slice)
Select #String = right(#String, Len(#String) - #Index)
If Len(#String) = 0 break
End
Return
End

Looped the items and got it done.
Select * into #temp_inputIds from dbo.Split(#InputIds,',')
DECLARE #ID varchar (50)
DECLARE IDs CURSOR LOCAL FOR select items from #temp_inputIds
OPEN IDs
FETCH NEXT FROM IDs into #ID
WHILE ##FETCH_STATUS = 0
BEGIN
Select #SQL = 'Select component_id,'+#ID+' as pub_id from component_presentations where CONTENT like ''%' + #ID + '%'''
FETCH NEXT FROM IDs into #ID
END
CLOSE IDs
DEALLOCATE IDs

Related

SSRS Report: get parameter data value and store it into variable in dataset

I have multi-valued parameter in my Report named #Animal which has ('Cat', 'Dog', 'Mouse').
inside dataset i need to get 'Cat', Dog', 'Mouse' and store it into #AnimalName table variable.
"Hard-Coded" way would be:
DECLARE #AnimalName TABLE (Name nvarchar (10))
INSERT INTO #AnimalName SELECT ('Cat');
INSERT INTO #AnimalName SELECT ('Dog');
INSERT INTO #AnimalName SELECT ('Mouse');
I know that I can use #Animal directly inside my dataset, the reason I'm doing this is because I'm trying to improve my report's performance. Many multi-valued parameters will make the report runs forever.
Does any one know how(the syntax) to get #Animal data values and stored it into a table variables #AnimalName inside dataset?
Thanks heaps!
Pass the comma delimited string into your stored procedure and in your stored proc use a table valued function to convert you multi-valued parameter into a table.
CREATE PROC GetAllAnimals
#AnimalList nvarchar(max)
AS
DECLARE #Animals TABLE (Animal nvarchar(10))
INSERT INTO #Animals SELECT * FROM dbo.fnGetValueListFromMultiSelect(#AnimalList)
and then use the #Animals table to inner join in your query
Functions declared below.
For Integer (or ID) values
CREATE FUNCTION [dbo].[fnGetIdListFromMultiSelect](#String nvarchar(MAX))
RETURNS #Results TABLE ([Id] int)
AS
BEGIN
DECLARE #Delimiter CHAR(1)
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(4000)
IF #String IS NULL RETURN
SET #Delimiter = ','
SET #INDEX = 1
WHILE #INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF #INDEX !=0
BEGIN
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
END
ELSE
SELECT #SLICE = #STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO #Results([Id]) VALUES(CAST(#SLICE AS INT))
-- BREAK OUT IF WE ARE DONE
IF LEN(#STRING) = 0 BREAK
END
RETURN
END
For string values
CREATE FUNCTION [dbo].[fnGetValueListFromMultiSelect](#String nvarchar(MAX))
RETURNS #Results TABLE ([Item] nvarchar(128) Primary Key)
AS
BEGIN
DECLARE #Delimiter CHAR(1)
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(4000)
SET #Delimiter = ','
SET #INDEX = 1
WHILE #INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF #INDEX !=0
BEGIN
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
END
ELSE
SELECT #SLICE = #STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO #Results([Item]) VALUES(#SLICE)
-- BREAK OUT IF WE ARE DONE
IF LEN(#STRING) = 0 BREAK
END
RETURN
END

SQL replace statement too slow

I have a replace statement that does something like this:
SELECT Distinct Forenames, Surname, dbUSNs.DateOfBirth, Datasetname,
dbUSNs.MoPIGrade, SourceAddress, VRM, URNs
FROM Person
WHERE ( Replace(Replace(Replace(Replace(Replace(Replace(Replace
(Replace(Replace(Replace(Replace(Replace(Replace(Replace
(Replace(Replace(Replace(Replace(Replace(Replace(Replace
(Replace(Replace(Replace(Replace
(Surname,'/',''''),'?',''''),'',''''),'^',''''),'{',''''),'}',''''),
'[',''''),']',''''),';',''''),'$',''''),'=',''''),'*',''''),
'#',''''),'|',''''),'&',''''),'#',''''),'\',''''),'<',''''),
'>',''''),'(',''''),')',''''),'+',''''),',',''''),'.',''''),
' ','''') LIKE 'OREILLY%')
Therefore even though OReilly is passed, O'Reilly will be found. However, this is too slow. Is there a better way of approaching it?
The problem isn't that REPLACE is "too slow", but that using it at all makes that part of the query unsargable, meaning that it can't use an index.
Wikipedia: Sargable
Basically you've forced a tablescan / indexscan, from top to bottom. On top of that you have the overhead of REPLACE.
If you want this query to run fast, I would instead do one of the following:
Create an additional column containing a searchable text version of the Surname
Create an indexed, materialized view with those REPLACE functions
If you want to simply remove all special characters it's easier to specify the valid characters and use a function to perform the cleansing.
This shows you how to clean the string to alphanumeric characters and spaces '%[^a-z0-9 ]%'
DECLARE #Temp nvarchar(max) ='O''Rielly la/.das.d,as/.d,a/.da.sdo23eu89038 !£$$'
SELECT #Temp
DECLARE #KeepValues AS VARCHAR(50) = '%[^a-z0-9 ]%'
WHILE PatIndex(#KeepValues, #Temp) > 0
SET #Temp = Stuff(#Temp, PatIndex(#KeepValues, #Temp), 1, '')
SELECT #Temp
Which would return: ORielly ladasdasdadasdo23eu89038
So you can write a function:
CREATE FUNCTION [dbo].[RemoveNonAlphaCharacters](#Temp VARCHAR(1000))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #KeepValues AS VARCHAR(50) = '%[^a-z0-9 ]%'
WHILE PatIndex(#KeepValues, #Temp) > 0
SET #Temp = Stuff(#Temp, PatIndex(#KeepValues, #Temp), 1, '')
RETURN #Temp
END
Then simply call it like so:
SELECT *
FROM Person
WHERE [dbo].[RemoveNonAlphaCharacters](Surname) LIKE 'OREILLY%'
If you don't want spaces, just change it to: '%[^a-z0-9]%'
Try this:
Create a function to split:
create function [dbo].[Split](#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
Use this in you where clause:
WHERE ((REPLACE(Surname, items, '') FROM dbo.Split('/,?,^,{,},[,],;,$,=,*,#,|,&,#,\,<,>,(,),+,.')) LIKE 'OREILLY%')
General approach - yes.
Make another field (NameNormalized)
Run a trigger that sets the field whenever the name is updated.
Then you can run a search on that field (which can have an index).
Bascially the whole replace orgy makes the whole thing non-indexable, so the better approach is to store the normalized value and allow fast lookups.
Oh, and evaluate whether the distinct is needed - that is another hugh slowdown.
SELECT Distinct Forenames, Surname, dbUSNs.DateOfBirth, Datasetname,
dbUSNs.MoPIGrade, SourceAddress, VRM, URNs
FROM Person
WHERE Surname LIKE 'O[/?^{}[];$=*#|#\<>()+.]R[/?^{}[];$=*#|#\<>()+.]E[/?^{}[];$=*#|#\<>()+.]I[/?^{}[];$=*#|#\<>()+.]L[/?^{}[];$=*#|#\<>()+.]L[/?^{}[];$=*#|#\<>()+.]Y%')
if you want to remove all special characters just using SUB STRING and While
DECLARE #str VARCHAR(100),#Len INT,#Pos INT = 1,#char char(1),#results varchar(100)
SET #str = 'O''Rielly la/.das.d,as/.d,a/.da.sdo23eu89038 !£$$'
SET #Len = LEN(#str)
Set #results = ''
WHILE #Pos < #Len
BEGIN
SET #char = SUBSTRING(#str,#Pos,1)
IF #char like '[a-z0-9]' or #char = ' '
BEGIN
SET #results = #results + #char
END
SET #Pos = #Pos + 1
END
select #results

SQL Server procedure declare a list

My SQL code is fairly simple. I'm trying to select some data from a database like this:
SELECT * FROM DBTable
WHERE id IN (1,2,5,7,10)
I want to know how to declare the list before the select (in a variable, list, array, or something) and inside the select only use the variable name, something like this:
VAR myList = "(1,2,5,7,10)"
SELECT * FROM DBTable
WHERE id IN myList
You could declare a variable as a temporary table like this:
declare #myList table (Id int)
Which means you can use the insert statement to populate it with values:
insert into #myList values (1), (2), (5), (7), (10)
Then your select statement can use either the in statement:
select * from DBTable
where id in (select Id from #myList)
Or you could join to the temporary table like this:
select *
from DBTable d
join #myList t on t.Id = d.Id
And if you do something like this a lot then you could consider defining a user-defined table type so you could then declare your variable like this:
declare #myList dbo.MyTableType
That is not possible with a normal query since the in clause needs separate values and not a single value containing a comma separated list. One solution would be a dynamic query
declare #myList varchar(100)
set #myList = '1,2,5,7,10'
exec('select * from DBTable where id IN (' + #myList + ')')
You can convert the list of passed values into a table valued parameter and then select against this list
DECLARE #list NVARCHAR(MAX)
SET #list = '1,2,5,7,10';
DECLARE #pos INT
DECLARE #nextpos INT
DECLARE #valuelen INT
DECLARE #tbl TABLE (number int NOT NULL)
SELECT #pos = 0, #nextpos = 1;
WHILE #nextpos > 0
BEGIN
SELECT #nextpos = charindex(',', #list, #pos + 1)
SELECT #valuelen = CASE WHEN #nextpos > 0
THEN #nextpos
ELSE len(#list) + 1
END - #pos - 1
INSERT #tbl (number)
VALUES (convert(int, substring(#list, #pos + 1, #valuelen)))
SELECT #pos = #nextpos;
END
SELECT * FROM DBTable WHERE id IN (SELECT number FROM #tbl);
In this example the string passed in '1,2,5,7,10' is split by the commas and each value is added as a new row within the #tbl table variable. This can then be selected against using standard SQL.
If you intend to reuse this functionality you could go further and convert this into a function.
I've always found it easier to invert the test against the list in situations like this. For instance...
SELECT
field0, field1, field2
FROM
my_table
WHERE
',' + #mysearchlist + ',' LIKE '%,' + CAST(field3 AS VARCHAR) + ',%'
This means that there is no complicated mish-mash required for the values that you are looking for.
As an example, if our list was ('1,2,3'), then we add a comma to the start and end of our list like so: ',' + #mysearchlist + ','.
We also do the same for the field value we're looking for and add wildcards: '%,' + CAST(field3 AS VARCHAR) + ',%' (notice the % and the , characters).
Finally we test the two using the LIKE operator: ',' + #mysearchlist + ',' LIKE '%,' + CAST(field3 AS VARCHAR) + ',%'.
Alternative to #Peter Monks.
If the number in the 'in' statement is small and fixed.
DECLARE #var1 varchar(30), #var2 varchar(30), #var3 varchar(30);
SET #var1 = 'james';
SET #var2 = 'same';
SET #var3 = 'dogcat';
Select * FROM Database Where x in (#var1,#var2,#var3);
If you want input comma separated string as input & apply in in query in that then you can make Function like:
create FUNCTION [dbo].[Split](#String varchar(MAX), #Delimiter char(1))
returns #temptable TABLE (items varchar(MAX))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end;
You can use it like :
Declare #Values VARCHAR(MAX);
set #Values ='1,2,5,7,10';
Select * from DBTable
Where id in (select items from [dbo].[Split] (#Values, ',') )
Alternatively if you don't have comma-separated string as input, You can try Table variable OR TableType Or Temp table like: INSERT using LIST into Stored Procedure

Building dynamic T-SQL query from a string argument in a sproc

Let's say I have a table which contains a varchar field:
CREATE TABLE [MyTable] (
[MyId] varchar(3) NOT NULL,
.....
)
The [MyId] column contains sequential alphanum values like A1, A2... A99, B1, B2..B99, C1 and so on (up to Z99).
What I'd like to do is to extract rows from the table whose MyId field matches some specific prefixes... e.g. I'd like to fetch rows from the series A, C, P and X.
And I'd like to this with a sproc which will dynamically construct the query based on the prefix alphabets supplied in the argument.
I'm thinking about something like this...
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray varchar(max)
AS
... -- split individual characters from #prefixArray into an array
SELECT * FROM [MyTable]
WHERE
[MyId] LIKE ....
OR
[MyId] LIKE .... -- iterate all characters from #prefixArray
I think the main bulk of the stored procedure will resemble the following pseudo-code:
DECLARE #sql nvarchar(max)
-- iterate through all the characters
SET #sql = 'SELECT * FROM [MyTable] WHERE [MyId] LIKE ' + #charInTheArray + '%'
SET #sql = #sql + ' OR [MyId] LIKE ' + #nextCharInArray + '%'
EXEC (#sql)
The above proecedure will be called like this:
EXEC uspFilterMyTable("A,C,P,X")
... or perhaps like this (if it makes splitting the alphabets easier):
EXEC uspFilterMyTable("ACPX")
Any ideas? Pointers?
Update: OK, this is what I've come up with ([Split] function borrowed from Chhatrapati Sharma):
-- [MyTable] contains these rows: 'A7', 'A87', 'B16', 'C51', 'H99', 'X12'
-- the "input" parameter
DECLARE #prefixArray NVARCHAR(100)= 'H,A,C'
-- split the string into SQL wild-card patterns
DECLARE charCursor CURSOR FOR
select items + N'%' from dbo.Split(#prefixArray, ',')
OPEN charCursor;
DECLARE #pattern CHAR(2)
-- create temp table if necessary
IF NOT EXISTS(SELECT * FROM TEMPDB.SYS.TABLES WHERE NAME LIKE '#tmpTable%')
CREATE TABLE #tmpTable ([Id] VARCHAR(3) NOT NULL)
-- purge old data
DELETE FROM #tmpTable
FETCH NEXT FROM charCursor into #pattern
WHILE ##FETCH_STATUS = 0
BEGIN
--SELECT * INTO #tmpTable FROM [MyTable] WHERE [MyId] LIKE #pattern
Insert Into #tmpTable Select * FROM [MyTable] WHERE [MyId] LIKE #pattern
FETCH NEXT FROM charCursor into #pattern
END
CLOSE charCursor;
DEALLOCATE charCursor;
-- return the values
SELECT * FROM #tmpTable
It's ugly I know, but it works... any tips to improvise the code?
first you should create below function and then use this in query like this
SELECT * FROM [MyTable] WHERE [MyId] in (select items from dbo.split(#prefixArray,','))
CREATE FUNCTION [dbo].[Split](#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
Here you have a nice and fast split method based on XML:
DECLARE #str NVARCHAR(100)= 'A1,B3,C4,B12,K19', #separator VARCHAR(1)= ','
DECLARE #SplitedList TABLE (code NVARCHAR(30))
DECLARE #XMLList XML
SET #XMLList=CAST('<i>'+REPLACE(#str, #separator,'</i><i>')+'</i>' AS XML)
INSERT INTO #SplitedList
SELECT x.i.value('(./text())[1]','varchar(100)')
FROM #XMLList.nodes('i') x(i)
SELECT * FROM #SplitedList
Result will be a table with the splitted values:
code
A1
B3
C4
B12
K19
From here you can continue and use this table on your procedure and join with you original table using LIKE as you propossed.
I would have suggested you to use table valued parameters to call your stored procedure. I guess you call it from .net. But EF I think will not be able to handle it, though you might check it. If not, I think the best way is to first parse the string into a temporary table, or a table value and after that join with it.
With TVP:
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray tvp_idlist readonly
as
select
t.*
from MyTable t
join #prefixArray pa on pa.id = t.myid
With a split function (of your choosing, you find many examples on the net)
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray varchar(max)
as
create #prefixArray tvp_idlist
insert into #prefixArray (id)
select id from dbo.myCustomSplit(#prefixArray,',')
select
t.*
from MyTable t
join #prefixArray pa on pa.id = t.myid
Where for both cases #prefixArray is a table variable is Id = varchar(3)
As an edit, after a little digging, it seems that with a little work EF works fine with TVPs. Check this : Entity Framework Stored Procedure Table Value Parameter. So The best thing is to send directly a table to your stored procedure, then to send a string to parse.

How to use IN Operator in SQL Server

How to use IN Operator in SQL Server
Here Is the table Structure
Create Table Sample(Id INT,Name Varchar(50))
While I am the Query like this I can get the Value
Select * FROM Sample WHERE Id IN ('74','77','79','80')
While I am executing the above Query I can't able to get the Records Related to that table getting error executing this error.
DECLARE #s VARCHAR(MAX)
SET #s='74','77','79','80'
Select * FROM Sample WHERE Id IN (#s)
You are using wrong way
use the following way
DECLARE #s VARCHAR(MAX)
DECLARE #d VARCHAR(MAX)
SET #s='74 , 77 , 79 , 80'
set #d = 'select * from arinvoice where arinvoiceid in('+#s+')'
exec (#d)
here IN operator use integers collection not string collection..
you should use a function which gives back a result set ( takes a csv format and returns a table)
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[Splitt] (#String NVARCHAR(4000),
#Delimiter CHAR(1))
RETURNS #Results TABLE (
Items NVARCHAR(4000))
AS
BEGIN
DECLARE #Index INT
DECLARE #Slice NVARCHAR(4000)
SELECT #Index = 1
IF #String IS NULL
RETURN
WHILE #Index != 0
BEGIN
SELECT #Index = Charindex(#Delimiter, #String)
IF #Index <> 0
SELECT #Slice = LEFT(#String, #Index - 1)
ELSE
SELECT #Slice = #String
IF ( NOT EXISTS (SELECT *
FROM #Results
WHERE items = #Slice) )
INSERT INTO #Results
(Items)
VALUES (#Slice)
SELECT #String = RIGHT(#String, Len(#String) - #Index)
IF Len(#String) = 0
BREAK
END
RETURN
END
and now you can write :
DECLARE #s VARCHAR(MAX)
SET #s='74,77,79,80'
Select * FROM Sample WHERE Id IN (select items from dbo.Splitt(#s,','))
If you are using ADO.NET, you can avoid the magic string, just use SqlDataRecord.
Or if you are using SQL Server 2008, you can also avoid the magic string by using Table-Valued Parameter
Source: http://www.sommarskog.se/arrays-in-sql-2008.html