SQL Set/Declare Variable - sql

I set my
declare #area as varchar(500)
set #area = '''Area1'',''Area2'''
I tried to put it in WHERE clause, but it doesn't detect area. I am guessing there is something wrong with set #area =?
This is what I'm trying to bring it in:
where area in (#area)
Please help, thanks.

Since you've got a list of elements to compare against, use a table variable to accumulate the comparison data, e.g.:
DECLARE #area as TABLE(Name VARCHAR(50));
INSERT INTO #area(Name) VALUES ('Area1'), ('Area2');
You can then use this either:
SELECT *
FROM MyTable
WHERE MyCol in (SELECT Name FROM #Area);
Or better, via a direct join (failed joins will be eliminated):
SELECT *
FROM MyTable INNER JOIN #Area
ON MyTable.MyCol = #Area.Name;

It should be a set rather then varchar variable:
where area in (select a from(values('Area1'),('Area2')) t(a))

Try something like
declare #area as varchar(500)
set #area = 'Area1,Area2'
SELECT *
FROM TableName
where area in (
SELECT Split.a.value('.', 'VARCHAR(100)') AS Area
FROM (SELECT CAST ('<M>' + REPLACE(#area, ',', '</M><M>')
+ '</M>' AS XML) AS String ) AS A
CROSS APPLY String.nodes ('/M') AS Split(a)
)

try use dynamic sql like-
declare #area as varchar(500)
set #area = 'Area1,Area2'
DECLARE #sql varchar(500)
set #sql='SELECT *
FROM [dbo].[tbl] AS t
WHERE area in ('+#area+')'
exec (#sql)
or
declare #area as varchar(500)
set #area = '''Area1'',''Area2'''
DECLARE #sql varchar(500)
set #sql='SELECT *
FROM [dbo].[tbl] AS t
WHERE area in ('+#area+')'
PRINT #sql
exec (#sql)

Result of your code
Query
declare #area as varchar(500)
set #area = '''Area1'',''Area2'''
Enter
print 'where area in (' + #area + ')'
Result
where area in ('Area1','Area2')

Related

Convert string from column to parameter in dynamic SQL?

I created a dictionary table with 'Condition' column where I store conditions for particular customers. Table's name is CustomerConditions:
Then I created dynamic SQL where I want to use this Condition:
declare
#TableName as nvarchar(10),
#FieldName as nvarchar(20),
#CustName as Nvarchar(50),
#Condition as NVARCHAR(MAX)
set #Condition = (SELECT o.Condition FROM CustomerConditions o WHERE o.Group = #CustName)
declare #strSQL as NVARCHAR(MAX)
SET #strSQL =
'DECLARE #FieldName as nvarchar(20),
#CustName as nvarchar(50)
;WITH NewCTE AS (
SELECT Tab1.Group, '+#FieldName+'
FROM (
SELECT
'+#Condition+' AS Group,
'+#FieldName+'
FROM '+#TableName+' as c) as Tab1)
SELECT * FROM NewCTE'
EXEC(#strSQL)
The problem is that when I pass #Condition to dynamic SQL string from column 'Condition' does not become part of SQL syntax - it's passed as expression, in the result I got:
This is not what I want. I want this 'Case WHEN...' to become part of my SQL syntax.
On the other hand when I'm not using #Condition but pass 'CASE WHEN..' explicitly then everything works well (all declared parameters works well, all is good):
;WITH NewCTE AS (
SELECT Tab1.Group, '+#FieldName+'
FROM (
SELECT
CASE WHEN '+ #FieldName +' LIKE ''XY%'' OR '+ #FieldName +' LIKE ''XYZ%'' then '''+ #CustName +''' END AS Group,
'+#FieldName+'
FROM '+#TableName+' as c) as Tab1)
So how can I pass this 'Case when' condition into dynamic SQL using parameter?
While agreeing with the issue of dynamic prone to injection attacks, and assuming some random values for your non initialized variables, here is how I approach
The trick is
SELECT #Condition = REPLACE(#Condition, '#FieldName', #FieldName )
SELECT #Condition = REPLACE(#Condition, '#CustName', #CustName )
So the main Query will be
declare
#TableName as nvarchar(10) = 'MyTbl',
#FieldName as nvarchar(20) = 'FldName',
#CustName as Nvarchar(50) = 'C1' ,
#Condition as NVARCHAR(MAX)
SELECT #Condition = (SELECT o.Condition FROM CustomerConditions o WHERE o.[Group] = #CustName)
SELECT #Condition = REPLACE(#Condition, '#FieldName', #FieldName )
SELECT #Condition = REPLACE(#Condition, '#CustName', #CustName )
declare #strSQL as NVARCHAR(MAX)
SET #strSQL = 'DECLARE #FieldName as nvarchar(20),
#CustName as nvarchar(50)
;WITH NewCTE AS (
SELECT Tab1.Group, '+#FieldName+'
FROM (
SELECT
'+#Condition+'
'+#FieldName+'
FROM '+#TableName+' as c) as Tab1)
SELECT * FROM NewCTE'
Select(#strSQL)
First provide proper values to your variables and execute this.
Instead of executing the query, what I did was create the query to see whether it got created as you want.
To be certain, you can copy paste the outcome and execute that.
Then you can change the last line to Execute ...

Select from a loop to a list of table that has similar name but with increment numbers added

I would like to use a query to loop through tables that are similar in names but added a number after that (ie. tableJan01, tableJan02, tableJan03, etc..., tableJan30)
Is there a way in SQL Server to use the same query statement while varying the table name within it. (similar to using parameter values) (need this to add different input to each different month's table)
declare #x nvarchar(50) ='abc'
declare #z int =1
while (#z<30)
BEGIN
SET #z = #z + 1;
select * from (#x)
END;
this shows error
Must declare the scalar variable "#CharVariable".
this script shows too syntax error
declare #x nvarchar(50) ='abc'
declare #z int =1
while (#z<30)
BEGIN
SET #z = #z + 1;
select * from (#x+#z)
END;
also, simple code like this doesn't work too
declare #x nvarchar(50) ='abc'
select * from #x
I agree with John Cappelletti that this requirement feels like a design flaw, however, to get your list of table names you can do something like this:
declare #x nvarchar(50) ='abc'
declare #z int =1
declare #ListOfTableNames TABLE (TableName nvarchar(50));
while (#z<30)
BEGIN
SET #z = #z + 1;
INSERT INTO #ListOfTableNames (TableName) VALUES (#x + CONVERT(NVARCHAR(20), #z))
END
SELECT * FROM #ListOfTableNames
To do dynamic SQL on these tables you could build a query string and then pass that string to the sp_executesql proc. You could put that logic in place of the line where we populate the table variable with the numbered table names. Like this:
declare #x nvarchar(50) ='abc'
declare #z int =1
declare #sql NVARCHAR(100)
while (#z<30)
BEGIN
SET #z = #z + 1;
SET #sql ='SELECT * FROM '+ (#x + CONVERT(NVARCHAR(20), #z))
EXEC sp_executesql #sql
END
I would completely avoid a WHILE loop just use some pattern matching:
DECLARE #Prefix sysname = N'abc';
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = STUFF((SELECT #CRLF +
N'SELECT *' + #CRLF +
--N' ,N' + QUOTENAME(t.[name],'''') + N' AS TableName' + #CRLF + --Uncomment if wanted
N'FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + N';'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE t.[name] LIKE #Prefix + '%'
AND t.[name] NOT LIKE #Prefix + N'%[^0-9]'
ORDER BY t.[name]
FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,2,N'');
--PRINT #SQL;
EXEC sp_executesql #SQL;
DB<>Fiddle
But John is right, you certainly have a design flaw here.
Using dynamic SQL, it would look something like this:
declare
#Base_table_name nvarchar(50) = 'my_table'
,#Step int = 1
,#SQL nvarchar(max);
while(#Step < 30)
begin
set #SQL = 'select * from ' + #Base_table_name + right('00' + cast(#Step as nvarchar(50)),2);
print(#SQL); --this displays the SQL that would be run
--exec(#SQL) --uncomment this to run the dynamic SQL
set #Step+=1;
end;
Alternatively, you can be more precise by using the sys.schemas and sys.tables tables like so:
declare
#Base_table_name sysname = 'my_table'
,#schema_name sysname = 'my_schema'
,#Step int = 1
,#StepCount int = 0
,#SQL nvarchar(max);
/* This will create a table variable and populate it with all the tables you'll want to query */
declare #tables_to_query table (Step int identity, SchemaName sysname, TableName sysname);
insert into #tables_to_query(SchemaName, TableName)
select
s.name
,t.name
from
sys.schemas s
inner join
sys.tables t on s.schema_id = t.schema_id
where
s.name = #schema_name --this will limit the tables to this schema
and t.name like #Base_table_name + '%' --this will look for any table that starts with the base table name
/* this loops through all the tables in the table variable */
while(#Step <= #StepCount)
begin
select
#SQL = 'select * from ' + quotename(SchemaName) + '.' + quotename(TableName)
from
#tables_to_query
where
Step = #Step
print(#SQL); --this displays the SQL that would be run
--exec(#SQL) --uncomment this to run the dynamic SQL
set #Step+=1;
end;
The dynamic SQL approaches laid out in other answers will certainly get the job done for you, but if you find you're querying all of these tables frequently, it might server you well to build out a VIEW and query that as needed.
In keeping with Larnu's suggestion of putting the source table name into the result set, I'd probably do something like this:
CREATE VIEW dbo.vwJan
AS
SELECT
'tableJan01' AS SourceTable,
<Column List>
FROM dbo.tableJan01
UNION ALL
...<28 other iterations>
SELECT
'tableJan30' AS SourceTable,
<Column List>
FROM dbo.tableJan30;
From there, you can go ahead and query them all to your heart's content with a single statement.
SELECT
SourceTable,
<Any other columns you're interested in>
FROM
vwJan;

map a string list in sqlserver like listagg

i try conver some string such as '1,2,3' to 'a,b,c' with the anwser:
select stuff(
(
select ',' + realname from sys_user
where ','+'1,2,3'+',' like '%,'+cast(u_id as varchar(10))+',%' for xml path('')
),1,1,'')
charindex is well done. but i want to create a more common function, so that i can convert in any relation such that.
i try a function :
create function [dbo].[fn_enum2str]
(
#enum as varchar(1000),
#table_name as varchar(100),
#origin_field as varchar(100),
#target_field as varchar(100)
)
as
begin
declare #result varchar(1000)
declare #sqlstr nvarchar(1000)
set #sqlstr = 'set #result = ('
set #sqlstr = #sqlstr + 'select stuff('
set #sqlstr = #sqlstr + '(select '','' + ' +#target_field+ ' from ' + #table_name
set #sqlstr = #sqlstr + ' where '','+#enum+','' like ''%,''+cast('+#origin_field+' as varchar)+'',%'' for xml path(''''))'
set #sqlstr = #sqlstr + ',1,1,''''))'
exec(#sqlstr)
return #result
end
it faild with error, as you know, it is not allow to exec a dynamic sql in function.
i want to
select dbo.fn_enum2str(a.uids,'sys_user','u_id', 'realname') from my_table a
--output 'a,b,c'
so, in my question, how can i create a function or a proc to deal it ?
Suppose you have SQL-SERVER2016 you can use string_split like this:
Test data
CREATE TABLE [dbo].[stringlist]([Numbers] [nvarchar](50) NULL)
Insert into dbo.Stringlist(numbers)
values('1,2,3,4,5,10')
SQL Function
alter function dbo.HinkyBase26( #Value as varchar(250) ) returns VarChar(250) as
begin
--declare #Value as varchar(50) = '13,14,1,2,5,14'
-- Notes: 'A' = 0. Negative numbers are not handled.
declare #Result as VarChar(250) = '';
declare #stringsplit table (numbers nvarchar(50),Letters varchar(1))
insert into #stringsplit(numbers,Letters)
select numbers = #Value ,CHAR(64 + value) as Letters from string_split(#Value,',')
select #Result = Letter from (
select numbers,Letter = STUFF((Select ', ' + Letters
from #stringsplit b
where b.numbers = a.numbers
FOR XML PATH('')),1,2,'')
from #stringsplit a
group by numbers
)z
return #Result
end
Execution of function
SELECT TOP (1000) [Numbers],dbo.HinkyBase26(Numbers)
FROM [LegOgSpass].[dbo].[stringlist]
SQL Stored Proc
Create PROC dbo.usp_convertnumberstostring
#stringvalue nvarchar(250)
AS
BEGIN
Create table #stringsplit (numbers nvarchar(50),Letters varchar(1))
insert into #stringsplit(numbers,Letters)
SELECT Numbers = #stringvalue,CHAR(64 + value) as Letters
from string_split(#stringvalue,',')
select numbers,Letter = STUFF((Select DISTINCT ', ' + Letters
from #stringsplit b
where b.numbers = a.numbers
FOR XML PATH('')),1,2,'')
from #stringsplit a
group by numbers
drop table #stringsplit
END
Execute SP
DECLARE #RC int
DECLARE #stringvalue nvarchar(250) = '1,5,6'
-- TODO: Set parameter values here.
EXECUTE #RC = [dbo].[usp_convertnumberstostring]
#stringvalue
GO
Result
SQL Script
Create table #stringsplit (numbers nvarchar(50),Letters varchar(1))
insert into #stringsplit(numbers,Letters)
SELECT Numbers,CHAR(64 + value) as Letters
FROM [LegOgSpass].[dbo].[stringlist] a
cross apply string_split(numbers,',')
select numbers,Letter = STUFF((Select DISTINCT ', ' + Letters
from #stringsplit b
where b.numbers = a.numbers
FOR XML PATH('')),1,2,'')
from #stringsplit a
group by numbers
Drop table #stringsplit
CREATE function [dbo].[fn_enum2str]
(
#enum as varchar(1000),
#table_name as varchar(100)
)
returns varchar(1000)
as
begin
declare #result varchar(1000)
if #enum is null
return ''
if #table_name = 'sys_user'
set #result = (
select stuff(
(
select ',' + realname from sys_user
where ','+#enum+',' like '%,'+cast(u_id as varchar(10))+',%' for xml path('')
),1,1,''
)
)
if #table_name = 'sys_attachment'
set #result = (
select stuff(
(
select ',/' + filepath from sys_attachment
where ','+#enum+',' like '%,'+cast(aid as varchar(10))+',%' for xml path('')
),1,1,''
)
)
return #result
end
GO
only way to deal it what i can think of, to switch which sql will be exec by a flag. when other relation apearance, add it to the switch list.
select
dbo.fn_enum2str(a.uids, 'sys_user') as names,
dbo.fn_enum2str(a.attachids, 'sys_attachment') as filepaths
from my_table a
so that it can be overlay. yes, it is difficult to remember stuff or for xml path or listagg(oracle), and result to a long sql, and i am lazy.😄
if you have any anwser better, tell me, thanks.

SQL Variable in XML Node

I have the following code:
DECLARE #x TABLE (item XML (document Galeries))
DECLARE #schemaname VARCHAR(100)
SET #schemaname = 'GaleriesSchem2'
INSERT into #x
SELECT '
<GaleriesSchem2>
<Image_1 OriginalName="Image">4814111.jpg</Image_1>
<Image_2 OriginalName="Image2">481411.jpg</Image_2>
</GaleriesSchem2>'
SELECT rref.value('.', 'varchar(MAX)') AS 'Value'
FROM #x
CROSS APPLY
item.nodes('//GaleriesSchem2/node()') AS Results(rref)
result:
1 | 4814111.jpg
2 | 481411.jpg
But I want to change the root element dynamically, for example:
item.nodes('//[local-name()=sql:variable("#schemaname")]/node()') AS Results(rref)
But this code doesn't work.
Use asterisk instead of double slash
DECLARE #x TABLE(item XML)
DECLARE #schemaname VARCHAR(100)
SET #schemaname = 'GaleriesSchem3'
INSERT into #x
SELECT '
<GaleriesSchem2>
<Image_1 OriginalName="Image">4814111.jpg</Image_1>
<Image_2 OriginalName="Image2">481411.jpg</Image_2>
</GaleriesSchem2>
<GaleriesSchem3>
<Image_1 OriginalName="Image">4814111_3.jpg</Image_1>
<Image_2 OriginalName="Image2">481411_3.jpg</Image_2>
</GaleriesSchem3>
'
SELECT rref.value('.', 'varchar(MAX)') AS 'Value'
FROM #x
CROSS APPLY
item.nodes('*[local-name()=sql:variable("#schemaname")]/node()') AS Results(rref)
See demo on SQLFiddle

Conversion failed when converting the varchar value to data type int

I have a varchar(1000) column declared as field that contains all numbers, as shown below.
And I want to execute the following script. I need this to work please
Declare #PostalCode varchar(1000)=0
set #PostalCode ='7005036,7004168,7002314,7001188,6998955'
Select hl.* From CountryLocation cl
INNER JOIN refPostalCodes pc ON pc.PostalCode = hl.PostalCode
where pc.Postalcode in (#PostalCode) and pc.notDeleted = 1
It looks like you want to use sp_executesql:
Declare #PostalCode varchar(1000)=0
set #PostalCode ='7005036,7004168,7002314,7001188,6998955'
declare #sql nvarchar(4000) //didn't count the chars...
select #sql = N'Select hl.* From CountryLocation cl
INNER JOIN refPostalCodes pc ON pc.PostalCode = hl.PostalCode
where pc.Postalcode in (' + #PostalCode + ') and pc.notDeleted = 1'
exec sp_executesql #sql
You need to be very careful about SQL injection when coding this way.