Variable in query doesn't work - sql

I'm facing some problem with a SQL query, I'm creating a variable :
declare #material varchar(500)
declare #typ varchar(200)
declare #strsql varchar(max)
set #typ = 'papier'
select #material = (SELECT + rtrim(ltrim([grupa])) + ','
FROM [test].[dbo].[GT]
WHERE (typ = #typ) FOR XML PATH(''))
set #material = left(#material, len(#material)-1)
set #material = replace(#material, ',' ,''',''')
set #material = '''' + #material + ''''
select #material
The output from variable is :
'test','test2'
And here is a little part of my main code :
SELECT
Number,
isnull(sum((case
when [Group] in ('test','test2')
then isnull(cast([Quantity] as int), 0)
end)), 0) as other
FROM
[dbo].[test-table]
which works correct but when I'm trying to do this like that :
SELECT
Number,
isnull(sum((case
when [Group] in (#material)
then isnull(cast([Quantity] as int), 0)
end)), 0) as other
FROM
[dbo].[test-table]
Output is wrong (different). Can anyone tell me why? It's kinda this same.

It's kind of the same, but it's not the same. What you are looking to get is: in ('test','test2'), what you get is: in (''test','test2'').
You will have to build the rest of your query dynamically, something like:
DECLARE #SQL NVARCHAR(MAX) = 'SELECT
Number
,isnull(sum((case when [Group] in ('+ #material + ')
then isnull(cast([Quantity] as int),0) end)),0) as other
from [dbo].[test-table]'
EXEC sys.sp_executesql #SQL
If you say the output is wrong, it's best to show the actual output and why it is wrong. Saying "Output is different comparing to first query - it should be the same" is not enough, in most cases.
edit:
If you don't want to use dynamic sql (which makes sense), you could do something like this (which is actually much more readable than dynamic):
SELECT Number
, ISNULL(SUM(data),0) as other
FROM (
SELECT Number
, CASE WHEN [Group] IN (SELECT grupa FROM [dbo].[GT] WHERE typ = #typ)
THEN ISNULL(CAST([Quantity] AS INT), 0)
END data
FROM [dbo].[test-table]
) TT
GROUP BY Number
I am making a lot of assumptions about your goal, your schema and your data here though. Without more info, this is probably the best I can do...(There may be some performance tweaking to be done, but this is the essence)

Try to use a function for split the string:
CREATE FUNCTION [dbo].[FN_SplitString](#String nvarchar(4000), #Delimiter char(1))
RETURNS #Results TABLE (Items nvarchar(4000))
AS
BEGIN
IF #String IS NULL RETURN
IF LTRIM(RTRIM(#String)) = '' RETURN
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(4000)
SELECT #INDEX = 1
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
Then use it in this way:
SELECT
Number
,isnull(sum((case when [Group] in (SELECT Items, FROM FN_SplitString(#material,','))
then isnull(cast([Quantity] as int),0) end)),0) as other
from [dbo].[test-table]

In SQL Server, if you pass a comma separated list as a variable in IN Clause in T-SQL, it would not give error but you will not even get expected result either.
There are two solutions to handle this:
Using Dynamic query
Using Split function

Related

Split string into letters separated by a full stop (SQL)

I am trying to write a stored procedure which accepts a string parameter and returns it with each character separated by a full stop.
So for example I want the SP to accept parameter DOG and return D.O.G.
I have tried to use the STRING_SPLIT function as follows:
select STRING_SPLIT(#myString, '')
but it doesn't seem to be compatible with the version of SQL I'm using (2014) (the error message says it is not a recognised function). Even if it did work I'm not sure how to then insert full stops.
It seems like there should be an easy way to do this but I just can't find it!
Please let me know if you need any further information.
Many thanks.
Misread the question at first. This answer uses NGRams8K to split the parameter into characters, and then FOR XML PATH to join it back up:
SELECT (SELECT token +'.'
FROM dbo.NGrams8k('DOG',1)
ORDER BY position
FOR XML PATH(''))
If I understood well this is the best approach I can think of right now, using the answer from this question T-SQL Split Word into characters
with cte as (
select
substring(a.b, v.number+1, 1) as col
,rn = ROW_NUMBER() over (order by (select 0))
from (select 'DOG' b) a
join master..spt_values v
on v.number < len(a.b)
where v.type = 'P'
)
select distinct
STUFF((SELECT '.' + col FROM cte order by rn FOR XML PATH('')),1,1,'') as col
from cte
There is no built-in function to accomplish what you are after, but you can easily do it using a simple while loop.
Iterate through each character in the string and use CONCAT:
DECLARE #InputString NVARCHAR(200);
DECLARE #Seperator CHAR(1);
DECLARE #OutputString NVARCHAR(MAX);
DECLARE #Counter INT;
SET #InputString = N'TestString';
SET #Seperator = '.';
SET #Counter = 1;
WHILE #Counter <= LEN(#InputString)
BEGIN
SET #OutputString = CONCAT(
#OutputString
, SUBSTRING(#InputString, #Counter, 1)
, #Seperator
);
SET #Counter = #Counter + 1;
END;
SELECT #OutputString;
You can use a simple while loop with stuff:
declare #s varchar(max) ='dog'
declare #counter int = 0
declare #len int = len(#s)
while #counter < #len - 1
begin
set #s = stuff(#s, #len - #counter, 0, '.')
set #counter = #counter + 1
end
select #s + '.' as result
Since your input is a short string performances should not be a great concern: I tested this solution with a 8000 char string and the result was returned instantly.
Result:

Using a comma-separated parameter in an IN clause

I have 'param1, param2, parma3' coming from SSRS to a stored procedure as a varchar parameter: I need to use it in a query's IN clause but then need to change its format like this first:
select *
from table1
where col1 in('param1', 'param2', 'param3')
What is the best way to reformat the parameter without creating functions and parameter tables?
Try this one, Just need to add commas at the beginning and at the end of #params string.
Declare #params varchar(100) Set #params = ',param1,param2,param3,'
Select * from t
where CHARINDEX(','+cast(col1 as varchar(8000))+',', #params) > 0
SQL FIDDLE
you can use split function and use it as in following way
here my split fnSplitString return splitdata
select * from tb1 where id in(select splitdata from dbo.fnSplitString((select col1 from tb12 where id=3),','))
create FUNCTION [dbo].[fnSplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output(splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
If you are using SQL 2016 and above string_split you can use.
-- #param is where you keep your comma separated values example:
declare #param = 'param1,param2,param3'
select * from table1 where col1 in (select TRIM(value) from string_split(#param,',')
More information about string_split check offical documemt
Furthermore, TRIM() is used to trim values from white spaces.
We can use STRING_SPLIT() in SQL SERVER
DECLARE #params varchar(max)= 'param1,param2,param3'
SELECT *
FROM table1
WHERE col1 IN (SELECT value FROM STRING_SPLIT( #params , ','))
"Best way" is arguable, but one classic approach that remains without "creating functions and table parameters" is to simply employ dynamic SQL in the stored procedure:
-- FORNOW: local to act as the SP param and arg
declare #values varchar(100) = 'param1, param2, param3'
-- Add opening and closing single quotes, then quotes around each
-- comma-separated list item.
select #values = '''' + REPLACE(#values, ', ', ''', ''') + ''''
-- FORNOW: for clarity/debugging
print #values
--'param1', 'param2', 'param3'
-- Run the desired query as dynamic SQL.
DECLARE #sql as nvarchar(250);
SET #sql = 'select * from table1 where col1 in (' + #values + ')';
EXEC sp_executesql #sql;
This assumes a couple things, though:
That commas in the list of values are followed by a space. Variations on this solution can address deviations in this respect of course, but it is important to be aware of this assumption.
That the comma-separated values do not themselves have commas in them – unlikely but worth mentioning since whether values will satisfy this constraint sometimes goes unconsidered.
Load the Params into a string and execute as an sql :
declare #param varchar(1000) = 'param1, param2, parma3'
declare #sql varchar(4000)
select #sql =
'select *
from table1
where col1 in(''' + replace(#param,',',''',''') + ''')'
-- print #sql -- to see what you're going to execute
exec sp_executesql #sql
DECLARE #params varchar(max) = '1,2,3,4'
SELECT * FROM table2 WHERE colId IN (SELECT value FROM SPLIT(#params,','))
Base on id we can find.

Append a specific character after each character of a string in sql server

Thanks to advise me for the below issue:
I am using below query to fetch the value of a column:
Select OptionList = case when isnull(AS_CIS_Code,'') <> '' then AS_CIS_Code
else ''
end
from added_services
AS_CIS_Code column is of varchar(10) in added_services table. It contains values like 'AB', 'ABC', 'GHKIK', 'UYTIOPJ' and so on which represents different codes.
Now I have to select these codes after modifying the above query so that '_' is appended after each character.
Like it should be fetched as 'A_B_', 'A_B_C_', 'G_H_K_I_K_', 'U_Y_T_I_O_P_J_'.
How should I implement it? Using a temp table will down the performance for one column only, so should I use while loop or please suggest me better alternatives.
Try this:
DECLARE #Input VARCHAR(100) = 'TESTING'
DECLARE #Pos INT = LEN(#Input)
WHILE #Pos > 1
BEGIN
SET #Input = STUFF(#Input,#Pos,0,'_')
SET #Pos = #Pos - 1
END
SELECT #Input
Output
T_E_S_T_I_N_G
UDF
CREATE FUNCTION PadStr(#Data VARCHAR(100)) RETURNS VARCHAR(200)
AS
BEGIN
DECLARE #Input VARCHAR(200) = #Data
DECLARE #Pos INT = LEN(#Input)
WHILE #Pos > 1
BEGIN
SET #Input = STUFF(#Input,#Pos,0,'_')
SET #Pos = #Pos - 1
END
RETURN #Input + '_'
END
Output
SELECT dbo.PadStr('TESTING') -- T_E_S_T_I_N_G_
You can split the string using a numbers table and the rebuild it using for xml path().
select isnull(C.Value, '') as AS_CIS_Code
from added_services as A
cross apply (
select substring(A.AS_CIS_Code, T.N, 1)+'_'
from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) as T(N)
where T.N <= len(A.AS_CIS_Code)
order by T.N
for xml path('')
) as C(Value)
SQL Fiddle
Try this
Create Function Code_Pad
(
#code varchar(max)
)
returns varchar(max)
as
begin
Declare #coding varchar(max) =''
Declare #i int = 1
WHILE(LEN(#Coding)<=2*len(#code)-1)
begin
Select #coding = #coding+SUBSTRING(#code,#i,1)+'_'
set #i=#i+1
end
return #coding
end
End of Function
Select OptionList = case when isnull(AS_CIS_Code,'') <> '' then dbo.Code_Pad(AS_CIS_Code) else ''
end
from added_services
May not be best solution, but works in one query using recursion
;WITH valCTE(Replaced,ToBeReplaced,Position)
AS
(
SELECT CAST(LEFT(AS_CIS_Code,1) + '_' AS VARCHAR(20))
,SUBSTRING(AS_CIS_Code,2,LEN(AS_CIS_Code)-1)
,1
FROM added_services
UNION ALL
SELECT CAST(Replaced + LEFT(ToBeReplaced,1) + '_' AS VARCHAR(20))
,SUBSTRING(ToBeReplaced,2,LEN(ToBeReplaced)-1)
,Position+1
FROM valCTE
WHERE LEN(ToBeReplaced)>0
)
SELECT TOP 1 LEFT(Replaced,LEN(Replaced)-1) -- remove last _
FROM valCTE
ORDER BY Position DESC

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