I have my sql query as follows which is giving the result by Capitalizing the given strings but I need the with single quote on each of the individual one how can I do that
DECLARE #Xml XML
DECLARE #Propercase VARCHAR(max)
DECLARE #delimiter VARCHAR(5)
SET #delimiter=' '
DECLARE #string nvarchar(max)='ABC,DEF,GHI,JKL'
SET #Xml = Cast(( '<String>'
+ #string+ '</String>' ) AS XML)
;WITH cte
AS (SELECT a.value('.', 'varchar(max)') AS strings
FROM #Xml.nodes('String') AS FN(a))
-- SELECT * FROM cte;
SELECT #ProperCase = Stuff((SELECT ' ' +'('+ + Upper(LEFT(strings, 1))
+ Upper(Substring(strings, 2, Len(strings)) + ')'
)
FROM cte
FOR xml path('')), 1, 1, '')
SELECT #ProperCase
Currently my output is as follows (ABC,DEF,GHI,JKL) I need this to be ('ABC','DEF','GHI','JKL')
DECLARE #Xml XML
DECLARE #Propercase VARCHAR(max)
DECLARE #delimiter VARCHAR(5)
SET #delimiter=' '
DECLARE #string nvarchar(max)='ABC,DEF,GHI,JKL'
SET #Xml = Cast(( '<String>'
+ #string+ '</String>' ) AS XML)
;WITH cte
AS (SELECT a.value('.', 'varchar(max)') AS strings
FROM #Xml.nodes('String') AS FN(a))
SELECT #ProperCase = '(' + REPLACE(STUFF((SELECT ','''+ Upper(LEFT(strings, 1))
+ Upper(Substring(strings, 2, Len(strings))) + ''')'
FROM cte
FOR xml path('')), 1, 1, ''),',',''',''')
SELECT #ProperCase
Note that all the STUFF really does is drop the leading comma
Double up the single quotes to be output as one single quote character while still being wrapped in single quotes. So four (4) single quotes will output a single quote as a String.
DECLARE #prelimiter VARCHAR(5)
DECLARE #delimiter VARCHAR(5)
SET #prelimiter = '''' --## Ouptuts single quote
SET #delimiter = ''', ''' --## Ouptuts single quote then comma then space then single quote
SELECT
STUFF(' ', 1, 1,
'(' + #prelimiter + 'ABC' + #delimiter + 'DEF' + #delimiter + 'GHI' + ''')'
) AS Example
Output: ('ABC', 'DEF', 'GHI')
Related
I have the following two strings:
DECLARE #Str1 VARCHAR(MAX) = 'John A Mak|Street Road UAE'
DECLARE #Str2 VARCHAR(MAX) = '[First Name],[Last Name],[Middle Name]|[Address1],[Address2]'
Note: Both strings are dynamic may comes with more or less values.
Expected result: Want to if any of the given text present in the given columns using the PATINDEX. The following PATINDEX statement gonna used in the WHERE clause of the SELECT statement.
PATINDEX('John',[First Name]) + PATINDEX('A',[First Name]) + PATINDEX('Mak',[First Name]) +
PATINDEX('John',[Last Name]) + PATINDEX('A',[Last Name]) + PATINDEX('Mak',[Last Name]) +
PATINDEX('John',[Middle Name]) + PATINDEX('A',[Middle Name]) + PATINDEX('Mak',[Middle Name]) +
PATINDEX('Street',[Address1]) + PATINDEX('Road',[Address1]) + PATINDEX('UAE',[Address1]) +
PATINDEX('Street',[Address2]) + PATINDEX('Road',[Address2]) + PATINDEX('UAE',[Address2]) > 0
My try:
DECLARE #Str1 VARCHAR(MAX) = 'John A Mak|Street Road UAE'
DECLARE #Str2 VARCHAR(MAX) = '[First Name],[Last Name],[Middle Name]|[Address1],[Address2]'
DECLARE #Length int = 0
DECLARE #Length1 int = 0
DECLARE #Length2 int = 0
DECLARE #Position int = 0
DECLARE #Position1 int = 0
DECLARE #Position2 int = 0
DECLARE #Value varchar(max)
DECLARE #Value1 varchar(max)
DECLARE #Value2 varchar(max)
DECLARE #P_Str2 VARCHAR(MAX) = ''
DECLARE #P_Str3 VARCHAR(MAX) = ''
DECLARE #P_Str1 VARCHAR(MAX) = ''
DECLARE #FinalString VARCHAR(MAX) = ''
SET #P_Str1 = #Str1+'|';
SET #P_Str3 = #Str2+'|';
IF OBJECT_ID('tempdb..#tempt', 'U') IS NOT NULL DROP TABLE #tempt;
CREATE TABLE #tempt(keywords varchar(max));
WHILE CHARINDEX('|', #P_Str3, #Position2+1)>0
BEGIN
set #Length2 = CHARINDEX('|', #P_Str3, #Position2+1) - #Position2
set #Value2 = SUBSTRING(#P_Str3, #Position2, #Length2)
SET #P_Str2 = #Value2+',';
PRINT('--'+#P_Str2);
--WHILE LOOP for creating string for PAT INDEX
WHILE CHARINDEX(',', #P_Str2, #Position+1)>0
BEGIN
set #Length = CHARINDEX(',', #P_Str2, #Position+1) - #Position
set #Value = SUBSTRING(#P_Str2, #Position, #Length)
WHILE CHARINDEX('|', #P_Str1, #Position1+1)>0
BEGIN
set #Length1 = CHARINDEX('|', #P_Str1, #Position1+1) - #Position1
set #Value1 = SUBSTRING(#P_Str1, #Position1, #Length1)
PRINT('Value1--'+#Value1);
PRINT('Value--'+#Value);
INSERT INTO #tempt
SELECT DISTINCT split.a.value('.', 'VARCHAR(100)') AS Keywords
FROM
(
SELECT CAST ('<S>' + REPLACE(ltrim(rtrim(#Value1)), ' ', '</S><S>') + '</S>' AS XML) AS Element
) AS a
CROSS APPLY Element.nodes ('/S') AS split(a)
WHERE split.a.value('.', 'VARCHAR(100)') <> '';
SET #FinalString += STUFF(( SELECT '(PATINDEX('''+keywords+''','+#Value+'),''''0'''') + '
FROM #tempt FOR XML PATH('')), 1,0, '');
DELETE FROM #tempt;
SET #Position1 = CHARINDEX('|', #P_Str1, #Position1+#Length1) +1
END
SET #Position = CHARINDEX(',', #P_Str2, #Position+#Length) +1
END
SET #Position2 = CHARINDEX('|', #P_Str3, #Position2+#Length2) +1
END
PRINT(#FinalString);
But unable to get the expected result.
This is not pretty dynamic SQL, however...
USE Sandbox;
DECLARE #Str1 VARCHAR(MAX) = 'John A Mak|Street Road UAE';
DECLARE #Str2 VARCHAR(MAX) = '[First Name],[Last Name],[Middle Name]|[Address1],[Address2]';
DECLARE #WHERE nvarchar(MAX);
SET #WHERE = STUFF((SELECT N' + ' + NCHAR(10) +
STUFF((SELECT N' + ' +NCHAR(10)+
N'PATINDEX(' + QUOTENAME(DSn.Item,'''') + N',' + DSc.Item + N')' --This trusts no injection.I don't like this.
FROM dbo.DelimitedSplit8K(DS1.Item,' ') DSn
CROSS APPLY dbo.DelimitedSplit8K(DS2.Item,',') DSc
ORDER BY DSc.ItemNumber, DSn.ItemNumber
FOR XML PATH(N'')),1,4,N'')
FROM dbo.DelimitedSplit8K (#Str1,'|') DS1
CROSS APPLY dbo.DelimitedSplit8K (#Str2,'|') DS2
WHERE DS1.ItemNumber = DS2.ItemNumber
ORDER BY DS1.ItemNumber
FOR XML PATH(N'')),1,4,N'') + N' > 0'
SELECT #WHERE;
This outputs:
PATINDEX('John',[First Name]) +
PATINDEX('A',[First Name]) +
PATINDEX('Mak',[First Name]) +
PATINDEX('John',[Last Name]) +
PATINDEX('A',[Last Name]) +
PATINDEX('Mak',[Last Name]) +
PATINDEX('John',[Middle Name]) +
PATINDEX('A',[Middle Name]) +
PATINDEX('Mak',[Middle Name]) +
PATINDEX('Street',[Address1]) +
PATINDEX('Road',[Address1]) +
PATINDEX('UAE',[Address1]) +
PATINDEX('Street',[Address2]) +
PATINDEX('Road',[Address2]) +
PATINDEX('UAE',[Address2]) > 0
Note the use of DelimitedSplit8k, which you'll need on your instance to get this done.
Edit/Note: This is not injection safe. Specifically because of + DSc.Item +. The OP, in their sample data, provides already quoted strings; it is therefore assumed that the strings are properly quoted; I.e. not "[" & ColumnName & "]" (that is still open to injection, as any ] passed won't be escaped). If the columnnames are not properly quoted elsewhere, I strongly suggest removing the brackets ([]) in the passed value and using + QUOTENAME(DSc.Item) + instead.
I wonder that;
in SQl, is it possible to not bring the columns which have no data (or zero value)?
Select * from PLAYER_TABLE where PLAYER_NAME='cagri'
it is bringing just 1 row. because there is only one player which PLAYER_NAME is "cagri".
And there are 30 columns for example statistics.
Score-Rebound-PlayedMinutes-Fauls etc....
Score=2
Rebound=0
PlayedMinutes=2
Fauls=0
and I want to see only [Score] and [PlayedMinutes] columns when call my query.
is it possible?
You can use this logic over a stored procedure in SQL
DDL
create table usr_testtable
(player varchar(30),col1 float, col2 float, col3 float, col4 float)
insert into usr_testtable
values ('Jordan',10,20,3,0)
Convert to Stored Proc
declare #playername varchar(30) = 'Jordan' --- pass this value
declare #ctr smallint = 2 -- start from ordinal 2
declare #maxctr smallint = (SELECT max(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'usr_testTable')
declare #columns varchar(max) = ''
declare #columnswithvalues varchar(max) = ''
declare #coltocheck varchar(30)
declare #mysql nvarchar(max)
declare #coloutput varchar(30)
while #ctr <= #maxctr
begin
SELECT #coltocheck = COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'usr_testTable'
and ORDINAL_POSITION = #ctr
set #mysql = N'select #output = ' + #coltocheck + ' from usr_testTable where player =''' + #playername + ''' and cast(' + #coltocheck +' as float) > 0'
EXECUTE sp_executesql #mysql,N'#output int OUTPUT',#output = #coloutput OUTPUT;
if #coloutput > 0
begin
set #columns = coalesce(#columns + ',' + #coltocheck,#columns)
set #columnswithvalues = coalesce(#columnswithvalues + char(13) + char(10) + #coltocheck + ' : ' + #coloutput,#columnswithvalues) --- text form
end
set #coloutput = ''
set #ctr= #ctr + 1
end
-- final result in table format
set #mysql = N'select player' + #columns + ' from usr_testTable where player =''' + #playername + ''' '
EXECUTE sp_executesql #mysql
-- final result in text format appended with columnname
select #columnswithvalues -- format to display in text
First create dynamic SQL to select all columns names in the table PLAYER_TABLE except PLAYER_NAME, then unpivot data from PLAYER_TABLE into table PLAYER_TABLE1, then you can search values <> 0 and select this column in second dynamic SQL.
DROP TABLE PLAYER_TABLE1
DECLARE #Player NVARCHAR(50);
DECLARE #columns NVARCHAR(max);
DECLARE #sql NVARCHAR(max);
DECLARE #columns2 NVARCHAR(max);
DECLARE #sql2 NVARCHAR(max);
SET #player='cagri'
SET #columns = Stuff((SELECT ','
+ Quotename(Rtrim(Ltrim(x.columns)))
FROM (SELECT COLUMN_NAME as columns FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME= 'PLAYER_TABLE' and COLUMN_NAME<>'PLAYER_NAME' ) AS x
ORDER BY X.columns
FOR xml path('')), 1, 1, '');
SET #sql = N' SELECT
PLAYER_NAME, Un_Pivot.Field, Un_Pivot.Value
INTO PLAYER_TABLE1
FROM
(
SELECT * FROM PLAYER_TABLE
) Data
UNPIVOT
(
Value FOR Field IN ('+#columns+')
) AS Un_Pivot';
EXECUTE sp_executesql #sql;
SET #columns2 = Stuff((SELECT ','
+ Quotename(Rtrim(Ltrim(y.Field)))
FROM (SELECT Field FROM PLAYER_TABLE1 WHERE VALUE<>0 AND PLAYER_NAME=#Player) AS y
ORDER BY y.Field
FOR xml path('')), 1, 1, '');
SET #sql2 = N'SELECT PLAYER_NAME,'+#columns2+'FROM PLAYER_TABLE WHERE PLAYER_NAME='+char(39)+#Player+char(39);
EXECUTE sp_executesql #sql2
So I have a string that is from search and it can contain multiple words I want to cutoff the words at 10 characters i.e.
DECLARE #SearchString varchar(255) = 'Administration Duplication'
becomes: 'Administra Duplicatio'
From here with few modifications:
Create Function dbo.[getFirstTenCharacters]
(
#String Varchar(Max)
)
RETURNS Varchar(Max)
BEGIN
Declare #Xml Xml
Declare #firsttenletter Varchar(Max)
Declare #delimiter Varchar(5)
SET #delimiter=' '
SET #Xml = cast(('<a>'+replace(#String,#delimiter,'</a><a>')+'</a>') AS XML)
;With CTE AS (SELECT A.value('.', 'varchar(max)') as [Column]
FROM #Xml.nodes('a') AS FN(a) )
SELECT #firsttenletter =Stuff((SELECT ' ' + LEFT([Column],10)
FROM CTE
FOR XML PATH('') ),1,0,'')
RETURN (#firsttenletter)
END
GO
SELECT dbo.[getFirstTenCharacters]('Administration Duplication');
WORKING DEMO
A rather simpler version would look something like.....
DECLARE #SearchString Varchar(255) = 'Administration Duplication'
;WITH X AS (
SELECT Split.a.value('.', 'VARCHAR(10)') Words
FROM
(SELECT Cast ('<X>' +
Replace(#SearchString, ' ', '</X><X>') + '</X>' AS XML) AS Data
) AS t CROSS APPLY Data.nodes ('/X') AS Split(a)
)
SELECT STUFF((SELECT ' ' + Words
FROM X
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,1,'')
Create a function to split the strings.
should look something like this:
CREATE FUNCTION [dbo].[SPLIT_STRING](
#TEXT varchar(8000)
,#COLUMN int
,#SEPARATOR varchar(1)
)RETURNS varchar(8000)
AS
BEGIN
DECLARE #POS_START int = 1
DECLARE #POS_END int = CHARINDEX(#SEPARATOR, #TEXT, #POS_START)
WHILE (#COLUMN >1 AND #POS_END> 0)
BEGIN
SET #POS_START = #POS_END + 1
SET #POS_END = CHARINDEX(#SEPARATOR, #TEXT, #POS_START)
SET #COLUMN = #COLUMN - 1
END
IF #COLUMN > 1 SET #POS_START = LEN(#TEXT) + 1
IF #POS_END = 0 SET #POS_END = LEN(#TEXT) + 1
RETURN SUBSTRING (#TEXT, #POS_START, #POS_END - #POS_START)
END
THEN DO:
SELECT LEFT([dbo].[SPLIT_STRING](COLNAME,1,' '),10)+LEFT([dbo].[SPLIT_STRING](COLNAME,2,' '),10)
How can I concatenate an arbitrary length of columns per row? I tried the following, but the function as is requires one to specify the column names:
SELECT CONCAT([C1], [C2], ...) FROM [dbo.table];
How can I achieve the same result without specifying each column name explicitly?
You'd need to use dynamic SQL. You can query the system catalg view sys.columns to get the column names, and then use SQL Server's XML Extension to concatenate the rows to a single string giving your final SQL to execute:
DECLARE #TableName SYSNAME = 'dbo.YourTable';
DECLARE #SQL NVARCHAR(MAX) = 'SELECT CONCAT(' +
STUFF(( SELECT ',' + QUOTENAME(c.Name)
FROM sys.columns c
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
ADDENDUM
If you want to delimit your columns, you can add a further concatenation while you are creating your column list:
DECLARE #TableName SYSNAME = 'dbo.YourTable',
#Delimiter VARCHAR(10) = ', ';
DECLARE #SQL NVARCHAR(MAX) = 'SELECT CONCAT(' +
STUFF(( SELECT ',''' + #Delimiter + ''',' + QUOTENAME(c.Name)
FROM sys.columns c
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, LEN(#Delimiter) + 5, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
ADDENDUM 2
To avoid the delimiter being added when the value is null, e.g instead of ending up with:
1,,,2,3
You simply get
1,2,3
You need to slightly amend the logic, before it was generating a query that was like:
CONCAT([C1], ',', [C2], ',', [C3])
Instead you want:
CONCAT([C1], ',' + [C2], ',' + [C3])
Because you are now using ',' + [C2] if [C2] is null, the result will be null, so the delimiter will be removed:
DECLARE #TableName SYSNAME = 'dbo.YourTable',
#Delimiter VARCHAR(10) = ', ';
DECLARE #SQL NVARCHAR(MAX) = 'SELECT CONCAT(' +
STUFF(( SELECT ',''' + #Delimiter + ''' + ' + QUOTENAME(c.Name)
FROM sys.columns c
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, LEN(#Delimiter) + 7, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
ADDENDUM 3
To remove the first column you can use ROW_NUMBER() on the sys.columns system catalog view, then exclude the first column:
DECLARE #TableName SYSNAME = 'dbo.YourTable',
#Delimiter VARCHAR(10) = ', ';
DECLARE #SQL NVARCHAR(MAX) = 'SELECT CONCAT(' +
STUFF(( SELECT ',''' + #Delimiter + ''' + ' + QUOTENAME(c.Name)
FROM ( SELECT name,
RowNumber = ROW_NUMBER() OVER(ORDER BY column_id)
FROM sys.columns c
WHERE [object_id] = OBJECT_ID(#TableName)
) AS c
WHERE c.RowNumber != 1 -- not first column
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, LEN(#Delimiter) + 7, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
You need to use Dynamic-SQL for this:
Warning:
I've used tempdb (tempdb.sys.columns) because I cannot create normal tables in demo. In your case use your normal database. And change condition to: WHERE object_id = OBJECT_ID('table_name').
LiveDemo
CREATE TABLE #tab(ID INT, C1 INT, C2 INT, C3 INT);
INSERT INTO #tab VALUES (1, 1,2,3), (2, 2,3,4);
DECLARE #cols NVARCHAR(MAX);
SET #cols = STUFF(
(SELECT ',' + QUOTENAME(name)
FROM tempdb.sys.columns
WHERE
object_id = (SELECT object_id
FROM tempdb.sys.objects
WHERE NAME like '#tab%' AND Type = 'U')
AND name LIKE 'C%'
ORDER BY column_id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
DECLARE #query NVARCHAR(MAX)=
N'SELECT ID, CONCAT(<placeholder>) AS concated_columns FROM #tab';
SET #query = REPLACE(#query, '<placeholder>', #cols);
EXEC [dbo].[sp_executesql]
#query;
EDIT:
If you need specific character between concatenated values use:
(SELECT ',' + CONCAT(QUOTENAME(name) , ','' ''' )
LiveDemo2
To concat all columns in arbitrary table:
DECLARE #Columns nvarchar(MAX)
SELECT #Columns = ISNULL(#Columns + ',','') + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TableName' AND TABLE_SCHEMA='dbo'
DECLARE #sql nvarchar(MAX)
SET #sql = N'SELECT CONCAT('+#Columns+')
FROM dbo.TableName'
EXEC sp_executesql #sql
Use dynamic SQL and remember to cater for nulls!
declare #sql nvarchar(max), #t sysname, #c sysname
select #sql = 'select ', #t = '[dbo].[CONTACTS]' /* <---- YOUR TABLE NAME HERE */
declare cols cursor for
select name from sys.columns where object_id = object_id(#t) order by column_id
open cols
fetch next from cols INTO #c
while ##FETCH_STATUS = 0
begin
select #sql = #sql + 'convert(nvarchar(max), isnull(' + #c + ', '''')) + '
fetch next from cols INTO #c
end
close cols
deallocate cols
select #sql = left(#sql, len(#sql)-2) + ' from ' + #t
exec sp_executesql #sql
If your table has a primary key column, you could use a correlated subquery like the example below. SqlFiddle here.
SELECT (
( SELECT *
FROM dbo.table1 AS t2
WHERE t2.C1 = t1.C1 --specify primary key column(s) here
FOR
XML PATH('test')
, TYPE
)).value('/test[1]', 'nvarchar(MAX)') AS ConcatenatedValue
FROM dbo.table1 AS t1;
I have a string like this "Planck, Albert, Bohr"
I want the output as "Bohr Albert Planck"
I want this done using SQL server. I saw many string split functions all return the data as a table and in the forward order. These don't server my purpose.
Any pointers will be helpful.
This what you want:
DECLARE #source VARCHAR(MAX)
DECLARE #dest VARCHAR(MAX)
DECLARE #lenght INT
SET #source = 'Planck, Albert, Bohr'
SET #dest = ''
WHILE LEN(#source) > 0
BEGIN
IF CHARINDEX(' ', #source) > 0
BEGIN
SET #dest = SUBSTRING(#source,0,CHARINDEX(' ', #source)) + ' ' + #dest
SET #source = LTRIM(RTRIM(SUBSTRING(#source,CHARINDEX(' ',
#source)+1,LEN(#source))))
END
ELSE
BEGIN
SET #dest = #source + ' ' + #dest
SET #source = ''
END
END
SELECT REPLACE(REPLACE(#dest,Char(44),''),Char(13), '')
Char(44) is the ASCII value for , so at last I am just replacing that character.
This will print Bohr Albert Planck.
If you use a split string function like this you can do something like this.
declare #S varchar(20) = 'Planck, Albert, Bohr'
select
(
select ltrim(s)+' '
from dbo.Split(',', #S)
order by pn desc
for xml path(''), type
).value('.', 'varchar(max)')
SQL Fiddle
Declare #inStr varchar(1000)='Planck, Albert, Bohr'
;WITH CTE AS(
select ltrim(rtrim(reverse(left(reverse(#instr),CHARINDEX(',',reverse(#instr),1)-1)))) as strg,RIGHT(reverse(#instr),LEN(reverse(#instr))-CHARINDEX(',',reverse(#instr),1)) as rem
union all
select CASE WHEN CHARINDEX(',',c.rem,1)>0 then ltrim(rtrim(reverse(left(rem,CHARINDEX(',',rem,1)-1)))) else reverse(rem) end,
CASE WHEN CHARINDEX(',',c.rem,1)>0 then RIGHT(c.rem,LEN(rem)-CHARINDEX(',',rem,1)) else '' end
from CTE c
where len(rem) > 0 --CHARINDEX(',',c.rem,1)>0
)
select stuff((select ' '+strg from CTE for xml path('')),1,1,'')