I am trying to create a dynamic query in SQL Server.
Input: #value= abc,def,en,
Output: MAX(abc) as abc, MAX(def) as def, MAX(en) as en
My efforts so far took me no where.
With CONVERT() and REPLACE() I achieved a bit but finding it difficult. Need help!
Try this:
declare #value varchar(50) = 'abc,def,en'
declare #result varchar(100) = ''
select #result = replace(#value,'abc', 'MAX(''abc'') as abc')
select #result = replace(#result,'def', 'MAX(''def'') as def')
select #result = replace(#result,'en', 'MAX(''en'') as en')
select #result
You can also do the replacements in one line by nesting the expressions.
EDIT: If you have variable values in #value, you can take the below approach:
Use a splitter function to get the individual values in the string as a list. You can take a look at this article for implementations.
Insert this list to a temp table.
Update the temp table as shown above.
Concatenate the values into a single string using STUFF like so:
select stuff((select ',' + val from #temp for xml path('')),1,1,'')
Try this:
DECLARE #Value VARCHAR(200) = 'abc,def,en'
DECLARE #Template VARCHAR(100) = 'MAX(''##'') as ##'
DECLARE #Result VARCHAR(1000) = ''
DECLARE #Data VARCHAR(100) = ''
WHILE LEN(#Value) > 0
BEGIN
SET #Data = REPLACE(LEFT(#Value, ISNULL(NULLIF(CHARINDEX(',', #Value),0), LEN(#Value))),',','')
SET #Result = #Result + REPLACE(#Template, '##', #Data)
IF CHARINDEX(',', #Value) > 0
BEGIN
SET #Result = #Result + ','
SET #Value = REPLACE(#Value,#Data + ',','')
END
ELSE
SET #Value = REPLACE(#Value,#Data,'')
END
SELECT #Result
Have a look at SQL User Defined Function to Parse a Delimited String
So you can do like
Declare #Value varchar(200) = 'abc,def,en'
Declare #Item varchar(20) = null
declare #Str varchar(1000)=''
WHILE LEN(#Value) > 0
BEGIN
IF PATINDEX('%,%',#Value) > 0
BEGIN
SET #Item = SUBSTRING(#Value, 0, PATINDEX('%,%',#Value))
-- SELECT #Item
IF(LEN(#Str)>0)
SET #Str = #Str + ', SELECT MAX('+#Item+') as ' +#Item
ELSE
SET#Str = #Str + ' SELECT MAX('+#Item+') as ' +#Item
SET #Value = SUBSTRING(#Value, LEN(#Item + ',') + 1, LEN(#Value))
END
ELSE
BEGIN
SET #Item = #Value
SET #Value = NULL
SET #Str = #Str + 'SELECT MAX('+#Item+') as ' + #Item
END
END
select #Str
See the fiddle sample here
Related
I have tried so many times but could not find the exact query yet.
The one I made works in few string but doesn't work in another(It is uncertain).
What i want is the word which has '.' in it like "abcde sfjhjk.dkjb sajb njdhf", what i want is "sfjhjk.dkjb" as result . This is just an example.
The query returns all letters in some cases while truncates few digits in other cases. You can check by providing different values.
I tried below :
This doesn't work:
DECLARE #QUERY VARCHAR(MAX)='
renewreque0_.amount AS AMOUNT48_,
renewreque0_.charge_type AS CHARGE3_48_,
renewreque0_.commission_rate AS COMMISSION4_48_
'
SET NOCOUNT ON;
DECLARE #TABLENAME TABLE(TABLE_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #COLUMNS_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #NAME VARCHAR(MAX),#ALIAS VARCHAR(MAX),#J_QUERY VARCHAR(MAX),#W_QUERY VARCHAR(MAX)
DECLARE #WHERE_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
WHILE CHARINDEX('.',#QUERY)>1
BEGIN
SET #NAME = REVERSE( SUBSTRING(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))+1,CHARINDEX(' ',#QUERY)) )
SET #ALIAS= REVERSE(LEFT(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))))
SET #ALIAS=LEFT(#ALIAS,CHARINDEX(' ',#ALIAS))
SET #NAME=LTRIM(RTRIM(#NAME))
SET #ALIAS=LTRIM(RTRIM(#ALIAS))
INSERT INTO #COLUMNS_JOIN SELECT #NAME+#ALIAS,#NAME,REVERSE(LEFT(REVERSE(#ALIAS),LEN(#ALIAS)-1))
SET #QUERY=REPLACE(#QUERY,#NAME+#ALIAS,'')
END
SELECT * FROM #COLUMNS_JOIN
This works:
DECLARE #QUERY VARCHAR(MAX)='
AND t8_.username LIKE ?
AND t4_.branch_id = ?
AND t1_.account_no = ?
AND t0_.remarks = ?
AND t0_.collect_from = ?
'
SET NOCOUNT ON;
DECLARE #TABLENAME TABLE(TABLE_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #COLUMNS_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
DECLARE #NAME VARCHAR(MAX),#ALIAS VARCHAR(MAX),#J_QUERY VARCHAR(MAX),#W_QUERY VARCHAR(MAX)
DECLARE #WHERE_JOIN TABLE(COL VARCHAR(MAX),COLUMN_NAME VARCHAR(MAX),ALIAS VARCHAR(MAX))
WHILE CHARINDEX('.',#QUERY)>1
BEGIN
SET #NAME = REVERSE( SUBSTRING(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))+1,CHARINDEX(' ',#QUERY)) )
SET #ALIAS= REVERSE(LEFT(REVERSE(#QUERY),CHARINDEX('.',REVERSE(#QUERY))))
SET #ALIAS=LEFT(#ALIAS,CHARINDEX(' ',#ALIAS))
SET #NAME=LTRIM(RTRIM(#NAME))
SET #ALIAS=LTRIM(RTRIM(#ALIAS))
INSERT INTO #COLUMNS_JOIN SELECT #NAME+#ALIAS,#NAME,REVERSE(LEFT(REVERSE(#ALIAS),LEN(#ALIAS)-1))
SET #QUERY=REPLACE(#QUERY,#NAME+#ALIAS,'')
END
SELECT * FROM #COLUMNS_JOIN
Can anybody please help.
I would first use an SplitString function (passing a blank space as delimiter), which returns as rows each word on a string, and then filter it to return just the words having a dot.
SQL Server 2016 already has one https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql and on older SQL Servers you can build your own : Splitting the string in sql server
set #SQLStr varchar(512) = 'abcde sfjhjk.dkjb sajb njdhf';
select value from string_split(#SQLStr, ' ')
where charindex('.', value) > 0
Alright just for fun
declare #str nvarchar(100) = 'abcde sfjhjk.dkjb sajb njdhf',
#pointIndex int
SET #pointIndex = (SELECT CHARINDEX('.',#str) )
SELECT RTRIM(LTRIM(SUBSTRING(#str, #pointIndex - CHARINDEX(' ',REVERSE(LEFT(#str,#pointIndex))) +1,CHARINDEX(' ',REVERSE(LEFT(#str,#pointIndex)))) -- left side
+SUBSTRING(#str,#pointIndex +1, CHARINDEX( ' ', SUBSTRING(#str,#pointIndex,len(#str) - #pointIndex) ) -1 )))
Needless to say i would not recommend this option because it is really hard to maintain. As Marc said your best option here is to split your string for blanks and find the ones with a '.'
Now if you dont have SQLServer 2016 here is a split function for you :
CREATE function [dbo].[Split]
(
#string nvarchar(max),
#delimiter nvarchar(20)
)
returns #table table
(
[Value] nvarchar(max)
)
begin
declare #nextString nvarchar(max)
declare #pos int, #nextPos int
set #nextString = ''
set #string = #string + #delimiter
set #pos = charindex(#delimiter, #string)
set #nextPos = 1
while (#pos <> 0)
begin
set #nextString = substring(#string, 1, #pos - 1)
insert into #table
(
[Value]
)
values
(
#nextString
)
set #string = substring(#string, #pos + len(#delimiter), len(#string))
set #nextPos = #pos
set #pos = charindex(#delimiter, #string)
end
return
end
And use it as such :
SELECT * FROM dbo.Split(REPLACE(#str,' ','/'),'/')
WHERE charindex('.', value) > 0
Note that i replace blanks by another value.
I need the script as shown in the expected output but whatever i do, whatever i use, i end up with script shown in current output.
DECLARE #query varchar(max),
#col varchar(250) = 'd1,d2,d3'
Set #query = 'INSERT INTO [dbo].[MYTABLE](
'+#col+'
)
SELECT ''~N'' AS ['+#col+']'
print #query
current output
INSERT INTO [dbo].[MYTABLE](
d1,d2,d3
)
SELECT '~N' AS [d1,d2,d3]
Expected output
INSERT INTO [dbo].[MYTABLE](
d1,d2,d3
)
SELECT '~N' AS [d1],
'~N' AS [d2],
'~N' AS [d3]
Should work for any data split by comma and do what You wanted.
DECLARE #query varchar(max),
#col varchar(250) = 'd1,d2,d3', #word varchar(250) =''
Set #query = 'INSERT INTO [dbo].[MYTABLE](
'+#col+'
)'
while CHARINDEX( ',', #col)> 1
BEGIN
set #word = SUBSTRING(#col, 0,CHARINDEX(',', #col))
set #col = SUBSTRING(#col,CHARINDEX( ',', #col)+1, LEN(#col))
set #query = #query +
' SELECT ''~N'' AS ['+#word+']'
END
set #query = #query +
' SELECT ''~N'' AS ['+#col+']'
print #query
Your table output is wrong and this type of solutions doesn't scale,try simplfying your schema to avoid this..
At a high level,this can be achieved by
1.splitting comma seperated values which will return a column list
2.Then Convert column to rows..
Demo:
I used one of the split string functions from here..
--create table #temp
--(
--d1 varchar,
--d2 varchar,
--d3 varchar
--)
declare #q varchar(10)
set #q='d1,d2,d3'
;with cte
as
(select * from
[dbo].[SplitStrings_Numbers](#q,',')
)
insert into #temp
select
max(case when item='d1' then item end) as d1,
max(case when item='d2' then item end) as d2,
max(case when item='d3' then item end) as d3
from cte
DECLARE #query varchar(max),
`#col varchar(250) = 'd1,d2,d3',`
`#col1 varchar(250) = ''`
`Set #query = 'INSERT INTO [dbo].[MYTABLE](`
` '+#col+'`
` )' `
`SELECT #col=COALESCE(#col+',','')`
` WHILE(CHARINDEX(',',#col) > 0)`
<br/>
`BEGIN`
`SET #col1= left(#col, charindex(',', #col+',')-1)`
<br/>
`set #query=#query+'SELECT ''~N'' AS ['+#col1+'],'`
<br/>
`IF(CHARINDEX(',',#col) > 0 )`
<br/>
`BEGIN`
<br/>
`SET #col = STUFF(#col, 1, CHARINDEX(',', #col+''), '') `
<br/>
`END`
<br/>
`ELSE`<br/>
`IF(LEN(#col)>0)`<br/>
`BEGIN`<br/>
`SET #col = STUFF(#col, 1,LEN(#col) , '') ` <br/>
`END`<br/>
`END`<br/>
`SET #query = STUFF(#query, len(#query), 1, '') `<br/>
`print #query`
My answer
Declare #SQLString varchar(max)
Declare #SQLString2 varchar(max)
DECLARE #col_by_dim varchar(8000) = 'd1,d2,d3,d4',
#dim_name1 varchar(250) = 'CarrierEnrollment'
Set #SQLString = 'INSERT INTO [dbo].[zzz_'+#dim_name1+'](
'+#col_by_dim+'
)
Select '
DECLARE #pos INT
DECLARE #len INT
DECLARE #value varchar(8000)
set #pos = 0
set #len = 0
WHILE CHARINDEX(',', #col_by_dim, #pos+1)>0
BEGIN
set #len = CHARINDEX(',', #col_by_dim, #pos+1) - #pos
set #value = SUBSTRING(#col_by_dim, #pos, #len)
set #SQLString2 = '
''~N'' AS ['+#value+'],'
set #pos = CHARINDEX(',', #col_by_dim, #pos+#len) +1
Set #SQLString = #SQLString + #SQLString2
END
print #SQLString + '
''~N'' AS '+ '['+SUBSTRING(#col_by_dim, #pos, #len)+']'
DECLARE #query varchar(max),
#col varchar(250) = 'd1,d2,d3',#value varchar(max)
set #query='INSERT INTO [dbo].[MYTABLE](
'+#col+'
)
SELECT '
declare c cursor for
select val from Split(#col, ',')
open c
fetch next from c into #value
while ##FETCH_STATUS=0
begin
Set #query = #query+ '''~N'''+' AS ['+#value+']'+','+CHAR(13)+char(9)+char(9)+char(9)+char(9)+char(9)+char(9)+char(9)
fetch next from c into #value
end
close c
deallocate c
set #query= substring(#query,1,len(#query))
print #query
I have a table that stores SQL queries. I retrieve a query according to a condition and store it in a variable:
---------------------------------------------------------
ID | Query
---------------------------------------------------------
1 | 'Select Id,Name from Student'
2 | 'Select Id,Name,Father_Name from Student'
3 | 'Select Id,Name,Email from Student_Detail'
....
---------------------------------------------------------
For example, a variable #sql might have the first query from above:
Declare #sql nvarchar(500)
set #sql = 'Select Id,Name from Student'
I execute this query using:
Exec(#sql)
The problem is, how do I know which columns are used in that query? I'm trying to achieve something like what ColdFusion does with query.ColumnList, which returns the column list used in that query.
Try this:
SELECT SUBSTRING(query,8,CHARINDEX('from',query)-9) AS ColumnList
FROM YourTable
this is a variant of mehdi lotfi solution but is equally weak in the sense that only gives you whatever is in between the select and from statements so if you have aliases or calculations or 'case' statements it will not work properly; it does work if the column list is straight forward comma separated columns:
SELECT LEFT(REPLACE(#SQL,'Select ',''), CHARINDEX(' from',REPLACE(#SQL,'Select ',''))) AS ColumnList
Finally i managed to solve at my own using this solution.
Declare #sql varchar(1000)
Declare #valueList nvarchar(500)
Declare #tbl Table(Name varchar(100))
Declare #selectPos Int
Declare #fromPos Int
Declare #len Int
Declare #pos Int
Declare #prevpos Int
Declare #Delimeter varchar(2)
set #sql = 'Select Id,Name,Father_Name from Student'
set #selectPos = CHARINDEX('Select ', #sql, 1)+7
set #fromPos = CHARINDEX('From ', #sql, 1)
set #len = #fromPos - #selectPos
set #valueList = SUBSTRING(#sql, #selectPos, #len)
set #Delimeter = ', '
set #pos = 1
set #prevpos = 0
while #pos > 0
Begin
set #pos = charIndex(#Delimeter, #valueList, #pos)
If #pos = 0
Begin
Insert into #tbl
Select SUBSTRING(#valueList,#prevPos + 1,LEN(#valueList) - #prevpos)
Break;
End
Insert into #tbl
Select SUBSTRING(#valueList,#prevPos + 1,#pos-#prevpos - 1)
set #prevpos = #pos
set #pos = #pos + 1
End
select * from #tbl
I googled a lot to wrap string with minimum defined length, but I am unable to get any solution.
I created my own function that can wrap text by given number of characters per line.
This post may help to others looking for same.
Function 1
Create FUNCTION [dbo].[fn_BraekTextInLines]
(
-- Add the parameters for the function here
#InString varchar(max),
#LineLength int
)
RETURNS nvarchar(max)
AS
BEGIN
if #LineLength <=0 or #LineLength> LEN(#InString)
return #InString
declare #tmp varchar(max)
declare #result varchar(max)
DECLARE #word varchar (max);
declare #addedInResult bit
DECLARE c CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR SELECT s FROM SplitMax(#InString,'');
OPEN c;
FETCH NEXT FROM c INTO #word;
--set #tmp =#word
WHILE ##FETCH_STATUS = 0
BEGIN
if LEN(#tmp + ' ' + #word) < #LineLength
begin
set #tmp = #tmp + ' ' + #word
set #addedInResult = 0
end
else
begin
set #result = isnull(#result, ' ') + CHAR(13) + RTRIM(LTRIM( #tmp))
set #tmp = #word
set #addedInResult = 1
end
FETCH NEXT FROM c INTO #word;
if ##FETCH_STATUS <> 0
begin
set #result = isnull(#result, ' ') + CHAR(13) + RTRIM(LTRIM( #tmp))
set #addedInResult = 1
end
END
CLOSE c;
DEALLOCATE c;
if #addedInResult=0
begin
set #result = isnull(#result, ' ') + CHAR(13) + RTRIM(LTRIM( #tmp))
end
return #result
END
Function 2
Create FUNCTION [dbo].[fn_WrapString]
(
-- Add the parameters for the function here
#InString varchar(max),
#LineLength int
)
RETURNS nvarchar(max)
AS
BEGIN
declare #result varchar(max)
declare #tmp varchar(max)
DECLARE #Line varchar (max);
DECLARE c CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR SELECT s FROM SplitMax(#InString,CHAR(13));
OPEN c;
FETCH NEXT FROM c INTO #Line;
WHILE ##FETCH_STATUS = 0
BEGIN
set #tmp = dbo.fn_BraekTextInLines(#Line,#LineLength)
set #result = isnull(#result,' ') + #tmp
FETCH NEXT FROM c INTO #Line;
END
CLOSE c;
DEALLOCATE c;
return Rtrim(Ltrim(#result))
END
Function 3
ALTER FUNCTION [dbo].[SplitMax](#String VARCHAR(max), #Delimiter CHAR(1))
RETURNS #temptable TABLE (s VARCHAR(max))
AS
BEGIN
DECLARE #idx INT
DECLARE #slice VARCHAR(max)
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(s) VALUES(#slice)
SET #String = RIGHT(#String,len(#String) - #idx)
IF len(#String) = 0 break
END
RETURN
END
Calling Function fn_WrapString to wrap the text
declare #name varchar(max)
set #name = 'Ine was King of Wessex from 688 to 726. He was'+ CHAR(13) +'unable to retain the territorial gains of his predecessor, Cædwalla, who had brought much of southern England under his'
print dbo.fn_WrapString(#name,60)
Output :
Ine was King of Wessex from 688 to 726. He was
unable to retain the territorial gains of his predecessor,
Cædwalla, who had brought much of southern England under
his
I also created a short version for warping the text in T-Sql
CREATE FUNCTION [dbo].[WrapText]
(
#List NVARCHAR(MAX),
#length INT
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #result AS NVARCHAR(MAX);
SELECT #result = CASE
WHEN #result IS NULL THEN
SUBSTRING(#List, number.Number - #length + 1, #length)
ELSE
#result + CHAR(13) + CHAR(10) + SUBSTRING(#List, number.Number - #length + 1, #length)
END
FROM
(
SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects
) number
WHERE number.Number <= LEN(#List) + #length - 1
AND number.Number % #length = 0;
RETURN #result;
END;
Calling Function:
SELECT dbo.WrapText('abcdefghijklmnopqrstuvwxyz',5)
If you are coping the result from grid, it will not past with line breaks as explained here
https://stackoverflow.com/a/59189881/1606054
I have a variable #text varchar which has some values separated by a symbol, whatever I chose it to be. Ex:
declare #text varchar
set #text='John^Marry^Smith^Ane^Sue^'
I need to delete some data, but because it is a different server and database (a very long story), I must specify the in the WHERE clause, the values from my string, something like this:
Delete Employers where employer_name in ('John','Marry','Smith','Ane','Sue')
Can this be done? Most of all without any other objects, like procedures or functions?
Best regards, Bogdan
Simplest way: generate your SQL query as a string, using replace to form your in list, then execute it.
declare #sqlquery nvarchar(max)
set #sqlquery = 'Delete Employers where employer_name in (''' + replace(#text, '^', ''',''') + ''')'
EXEC sp_executesql #sqlquery
IF I understand your question correctly, then the answer is yes, just at you have stated it. You can use the following strip to turn a string into a table. If you declare the return table as a table variable, then you can roll it into your script as a
DELETE where EXISTS(....)
Create function [dbo].[atf_BarListToTable]
(#list ntext)
RETURNS #tbl TABLE (ListPosn int IDENTITY(1, 1) NOT NULL,
SString VARCHAR(1028) NOT NULL) AS
BEGIN
DECLARE #pos int
DECLARE #textpos int
DECLARE #ChunkLength smallint
DECLARE #str nvarchar(4000)
DECLARE #tmpstr nvarchar(4000)
DECLARE #leftover nvarchar(4000)
SET #textpos = 1
SET #leftover = ''
WHILE #textpos <= datalength(#list) / 2
BEGIN
SET #ChunkLength = 4000 - datalength(#leftover) / 2
SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #ChunkLength))
SET #textpos = #textpos + #ChunkLength
SET #pos = charindex('|', #tmpstr)
WHILE #pos > 0
BEGIN
SET #str = substring(#tmpstr, 1, #pos - 1)
INSERT #tbl (SString) VALUES( #str)
SET #tmpstr = ltrim(substring(#tmpstr, #pos + 1, len(#tmpstr)))
SET #pos = charindex('|', #tmpstr)
END
SET #leftover = #tmpstr
END
IF ltrim(rtrim(#leftover)) <> ''
INSERT #tbl (SString) VALUES(#leftover)
RETURN
END