I have the following variable to generate dynamic string.
Example:
If the variable contains two values:
DECLARE #Dynamic_Variables varchar(max) = 'One,Two'
DECLARE #SQL varchar(max)
SET #SQL = '( CASE WHEN [One] > 0 THEN 1 ELSE 0 END ) + ( CASE WHEN [Two] > 0 THEN 1 ELSE 0 END ) AS [Total]'
PRINT(#SQL)
The string should be like this:
( CASE WHEN [One] > 0 THEN 1 ELSE 0 END ) + ( CASE WHEN [Two] > 0 THEN 1 ELSE 0 END ) AS [Total]
If the string contains three values:
DECLARE #Dynamic_Variables varchar(max) = 'One,Two,Three'
DECLARE #SQL varchar(max)
SET #SQL = '( CASE WHEN [One] > 0 THEN 1 ELSE 0 END ) + ( CASE WHEN [Two] > 0 THEN 1 ELSE 0 END ) + ( CASE WHEN [Three] > 0 THEN 1 ELSE 0 END ) AS [Total]'
PRINT(#SQL)
The string should be like this:
( CASE WHEN [One] > 0 THEN 1 ELSE 0 END ) + ( CASE WHEN [Two] > 0 THEN 1 ELSE 0 END ) + ( CASE WHEN [Three] > 0 THEN 1 ELSE 0 END ) AS [Total]
Try the below query: (Change the #string value based on your requirement)
Take a look at the demo, If needs clarification.
DECLARE #string VARCHAR(MAX),
#Split CHAR(1),
#X xml
SELECT #string = 'One,Two,Three',
#Split = ','
SELECT #X = CONVERT(xml,'<root><s>'
+ REPLACE(#string,#Split,'</s><s>') + '</s></root>')
SELECT STUFF((SELECT ' + ' + Result1 AS FinalResult FROM
(SELECT '( CASE WHEN ['+ Result +'] > 0 THEN 1 ELSE 0 END )'
AS Result1
FROM
(SELECT T.c.value('.','varchar(max)') AS Result
FROM #X.nodes('/root/s') T(c)) AS A )
AS B FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 3, '') + '
AS [Total]'
AS ExpectedResult
Live Demo here
Use this.
DECLARE #Dynamic_Variables varchar(max)
DECLARE #SQL varchar(max)
SET #Dynamic_Variables = 'One,Two, Three'
SELECT STUFF((SELECT '+' + Value FROM
(
SELECT '( CASE WHEN [' + A.Value + '] > 0 THEN 1 ELSE 0 END )' AS Value
FROM
(
SELECT
Split.a.value('.', 'VARCHAR(100)') AS Value
FROM
(
SELECT CAST ('<M>' + REPLACE(#Dynamic_Variables, ',',
'</M><M>') + '</M>' AS XML) AS Value
) AS A
CROSS APPLY Value.nodes ('/M') AS Split(a)
) AS A
) AS B
FOR XML PATH (''), type).value('.', 'Varchar(max)'),1,1,'') + ' AS [Total]'
Related
Using SQL Server, I need to get a column count that has -1, 0, 1, 2 for its values for each row. There are different number of columns per table (one table has 55 fields)
-1 value = no answer or unknown (unknown_count)
0 value = ok (ok_count)
1 value = bad (bad_count)
2 value = not applicable (na_count)
row 1 looks like this
rowid name field_1 field_2 field_3 field_4
1 line_1 -1 1 2 1
2 line_2 2 1 -1 0
etc...
Results i would like to see
rowid na_count ok_count bad_count unknown_count
1 1 0 2 1
2 1 1 1 1
Extra credit question.... i need to get a count of all the fields that are used to build the counts (so i can give a percentile report)
You need a combination of UNPIVOT and some aggregation. Unpivot the data to a more sensible form, and then group by rowid summing up the various values:
with data AS
(
select
rowid,
field,
value
from
( SELECT rowid, field_1,field_2,field_3,field_4
FROM MyTable) p
UNPIVOT
( value FOR field IN (field_1,field_2,field_3,field_4) ) as unpvt
)
SELECT
rowid,
SUM(CASE WHEN value = 2 THEN 1 ELSE 0 END) AS na_count,
SUM(CASE WHEN value = 0 THEN 1 ELSE 0 END) AS ok_count,
SUM(CASE WHEN value = 1 THEN 1 ELSE 0 END) AS bad_count,
SUM(CASE WHEN value = -1 THEN 1 ELSE 0 END) AS unknown_count
from data
group by rowId
Live example: http://www.sqlfiddle.com/#!6/b702c/1
Building on the answer by Jamiec and some code I've used before; If you don't want to type in all the column names in the query you can use dynamic sql to build the query like this:
declare #tab nvarchar(max)
set #tab = N'your_table' -- change to your table name
declare #cols nvarchar(max)
select #cols = coalesce(#cols+N',', N'') + quotename(c.name) from syscolumns c
inner join sysobjects o on c.id = o.id and o.xtype = 'u'
where o.name = #tab
and c.name not in ('rowid', 'name') -- exclude the columns that don't hold data values
order by c.colid
declare #sql nvarchar(max)
select #sql = N'
select
rowid,
sum(case when val = 2 then 1 else 0 end) as ''na_count'',
sum(case when val = 0 then 1 else 0 end) as ''ok_count'',
sum(case when val = 1 then 1 else 0 end) as ''bad_count'',
sum(case when val = -1 then 1 else 0 end) as ''unknown_count'',
count(*) as column_count
from (select rowid, ' + #cols + N' from ' + #tab + N') as src
unpivot (val for col in (' + #cols + N')) as unpvt
group by rowid'
exec sp_executesql #sql
I think it will be more effective to sum up all the columns in a dynamical script, rather than to use unpivot. On one million rows i get 16% vs 84% relative to the batch in the execution plan (this codeĀ“s output #query vs the unpivot code).
You can use the same logic to get the percentages of each column. Please let me know if you want me to provide you with code for that as well.
--DROP TABLE TMP_Test
CREATE TABLE TMP_Test
(
rowid INT PRIMARY KEY IDENTITY(1,1)
, name varchar(10)
, field_1 INT
, field_2 INT
, field_3 INT
, field_4 INT
)
INSERT INTO TMP_Test
SELECT name = 'line_1', field_1 = -1, field_2=1, field_3=2, field_4=1
UNION ALL
SELECT name = 'line_2', field_1 = 2, field_2=1, field_3=-1, field_4= 0
/*
WHILE((SELECT COUNT(*) FROM TMP_Test) < 1000000)
BEGIN
INSERT INTO TMP_Test
SELECT name, field_1, field_2, field_3, field_4 FROM TMP_Test
END
*/
GO
DECLARE #query VARCHAR(MAX) = '';
DECLARE #schema VARCHAR(128) = 'dbo';
DECLARE #table VARCHAR(128) = 'TMP_Test';
DECLARE #na_count VARCHAR(max) = '';
DECLARE #ok_count VARCHAR(max) = '';
DECLARE #bad_count VARCHAR(max) = '';
DECLARE #unknown_count VARCHAR(max) = '';
SELECT
#na_count = #na_count + IIF(#na_count = '', '', ' + ') + 'IIF(' + COLUMN_NAME + ' = 2, 1, 0)'
, #ok_count = #ok_count + IIF(#ok_count = '', '', ' + ') + 'IIF(' + COLUMN_NAME + ' = 0, 1, 0)'
, #bad_count = #bad_count + IIF(#bad_count = '', '', ' + ') + 'IIF(' + COLUMN_NAME + ' = 1, 1, 0)'
, #unknown_count = #unknown_count + IIF(#unknown_count = '', '', ' + ') + 'IIF(' + COLUMN_NAME + ' = -1, 1, 0)'
FROM INFORMATION_SCHEMA.COLUMNS WHERE
TABLE_SCHEMA = #schema
AND TABLE_NAME = #table
AND COLUMN_NAME NOT IN ('rowid', 'name')
ORDER BY ORDINAL_POSITION;
SET #query = '
SELECT
rowid
, na_count = ' + #na_count + '
, ok_count = ' + #ok_count + '
, bad_count = ' + #bad_count + '
, unknown_count = ' + #unknown_count + '
FROM [' + #schema + '].[' + #table + ']';
PRINT(#query);
EXEC(#query);
This question already has answers here:
Finding Uppercase Character then Adding Space
(3 answers)
Closed 8 years ago.
How do you prepends space in a string where Upper Case letter comes or where a space really needed.
The Sample code is:
DECLARE #teams TABLE (Team NVARCHAR(100))
INSERT INTO #teams
SELECT 'TataConsultencyServices'
UNION ALL
SELECT 'TataConsultencyCompany'
UNION ALL
SELECT 'CompanyHumanResource'
Expected Result
Tata Consultency Services
Tata Consultency Company
Company Human Resource
A set based solution:
DECLARE #s NVARCHAR(100);
SET #s = 'CompanyHumanResources';
DECLARE #Idx INT = 1;
WITH CteRecursive
AS
(
SELECT 1 AS Idx,
CONVERT(NVARCHAR(200), #s) AS String
UNION ALL
SELECT src.Idx + src.IsUpper + 1,
CONVERT(NVARCHAR(200),
CASE WHEN src.IsUpper = 1 THEN STUFF(src.String, src.Idx+1, 0, ' ') ELSE src.String END
)
FROM
(
SELECT rec.*,
CASE WHEN SUBSTRING(rec.String, rec.Idx, 1) <> ' ' AND SUBSTRING(rec.String, rec.Idx+1, 1) LIKE '[A-Z]' AND SUBSTRING(rec.String, rec.Idx+1, 1) COLLATE Romanian_CS_AS = UPPER(SUBSTRING(rec.String, rec.Idx+1, 1)) COLLATE Romanian_CS_AS THEN 1 ELSE 0 END AS IsUpper
FROM CteRecursive rec
WHERE rec.Idx + 1 <= LEN(rec.String)
) src
)
SELECT TOP(1) x.String
FROM CteRecursive x
ORDER BY x.Idx DESC;
Results:
String
-----------------------
Company Human Resources
You may surely get some help from this:-
CREATE FUNCTION CaseSensitiveSQLSplitFunction
(
#str nvarchar(max)
)
returns #t table (val nvarchar(max))
as
begin
declare #i int, #j int
select #i = 1, #j = len(#str)
declare #w nvarchar(max)
while #i <= #j
begin
if substring(#str,#i,1) = UPPER(substring(#str,#i,1)) collate Latin1_General_CS_AS
begin
if #w is not null
insert into #t (val) select #w
set #w = substring(#str,#i,1)
end
else
set #w = #w + substring(#str,#i,1)
set #i = #i + 1
end
if #w is not null
insert into #t (val) select #w
return
end
Taking the sample as:-
declare #str nvarchar(max) = N'ThisIsATest'
select * from dbo.CaseSensitiveSQLSplitFunction(#str)
set #str = N'ThisIsASqlServerCaseSensitiveSplitStringFunction'
select * from dbo.CaseSensitiveSQLSplitFunction(#str)
It is now possible to sql concatenate string values in a way from rows to single column value.
We can just use any of the sql concatenation function.
declare #str nvarchar(max) = N'ThisIsATest'
SELECT LTRIM(STUFF((
SELECT ' ' + val FROM dbo.CaseSensitiveSQLSplitFunction(#str) FOR XML PATH('')
), 1, 1, '')) string
set #str = N'ThisIsASqlServerCaseSensitiveSplitStringFunction'
SELECT LTRIM(STUFF((
SELECT ' ' + val FROM dbo.CaseSensitiveSQLSplitFunction(#str) FOR XML PATH('')
), 1, 1, '')) string
WHILE 1 = 1
BEGIN
UPDATE #teams
SET TeamName = STUFF(TeamName, patindex('%[a-z,.][A-Z]%', TeamName COLLATE Latin1_General_BIN) + 1,0,' ')
WHERE patindex('%[a-z,.][A-Z]%', TeamName COLLATE Latin1_General_BIN) > 0
IF ##ROWCOUNT = 0 BREAK
END
UPDATE #teams
SET TeamName = STUFF(TeamName, patindex('%[A-Z][a-z]%', RIGHT(TeamName,LEN(TeamName) -1) COLLATE Latin1_General_BIN) +1 ,0,' ')
WHERE patindex('%[A-Z][a-z]%', TeamName COLLATE Latin1_General_BIN) > 0
I have two strings in SQL Server.
For example:
declare #str1 as varchar(max)
declare #str2 as varchar(max)
set #str1 ='10:00am,2:00pm'
set #str2 = '10:00am,12:00pm,2:00pm,4:00pm,6:00pm,8:00pm'
and I want to compare the two strings and want to get those elements string from #str2 which are not in #str1.
That means the result should be :
#str3 = '12:00pm,4:00pm,6:00pm,8:00pm'
Try this :-
declare #str1 as varchar(max)
declare #str2 as varchar(max)
set #str1 ='10:00am,2:00pm'
set #str2 = '10:00am,12:00pm,2:00pm,4:00pm,6:00pm,8:00pm'
--the below 2 CTE's are used for splitting the string into different rows
;with cteStr1(str1) as
(
SELECT
RIGHT(LEFT(#str1,Number-1),
CHARINDEX(',',REVERSE(LEFT(','+#str1,Number-1)))) as str1
FROM
master..spt_values
WHERE
Type = 'P' AND Number BETWEEN 1 AND LEN(#str1)+1
AND
(SUBSTRING(#str1,Number,1) = ',' OR SUBSTRING(#str1,Number,1) = '')
),cteStr2(str2) as
(
SELECT
RIGHT(LEFT(#str2,Number-1),
CHARINDEX(',',REVERSE(LEFT(','+#str2,Number-1)))) as str2
FROM
master..spt_values
WHERE
Type = 'P' AND Number BETWEEN 1 AND LEN(#str2)+1
AND
(SUBSTRING(#str2,Number,1) = ',' OR SUBSTRING(#str2,Number,1) = '')
)
Select str2 from cteStr2
except
select str1 from cteStr1
Try this
DECLARE #str1 VARCHAR(MAX)
DECLARE #str2 VARCHAR(MAX)
SET #str1 ='10:00am,2:00pm'
SET #str2 = '10:00am,12:00pm,2:00pm,4:00pm,6:00pm,8:00pm'
SET #str1 = ',' + #str1 + ','
SET #str2 = ',' + #str2 + ','
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #str1) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #str1)
SELECT #name = SUBSTRING(#str1, 1, #pos-1)
SELECT #str1 = SUBSTRING(#str1, #pos+1, LEN(#str1)-#pos)
IF #name <> ''
BEGIN
SET #str2 = REPLACE(#str2,','+#name,'')
END
END
SET #str2 = REPLACE(#str2,','+#name,'')
SELECT SUBSTRING(#str2, 2, LEN(#str2)-2) AS Result
Try this one -
DECLARE
#str1 VARCHAR(500)
, #str2 VARCHAR(500)
SELECT
#str1 = '10:00am,2:00pm'
, #str2 = '10:00am,12:00pm,2:00pm,4:00pm,6:00pm,8:00pm'
;WITH cte AS
(
SELECT
id = p.value('(./n)[1]', 'INT')
, tm = p.value('(./s)[1]', 'VARCHAR(500)')
FROM (
SELECT field = CAST('<r><s>' + REPLACE(SUBSTRING(t.string + ',', 1, LEN(t.string + ',')), ',', '</s><n>' + CAST(t.id AS VARCHAR(10)) + '</n></r><r><s>') + '</s></r>' AS XML)
FROM (
SELECT string = #str1, id = 1
UNION ALL
SELECT #str2, 2
) t
) d
CROSS APPLY field.nodes('/r') t(p)
WHERE t.p.exist('n') = 1
)
SELECT tm FROM cte WHERE id = 2
EXCEPT
SELECT tm FROM cte WHERE id = 1
Or try this -
;WITH cte2 AS
(
SELECT
t.id
, tm =
SUBSTRING(
',' + t.string + ','
, number + 1
, CHARINDEX(',', ',' + t.string + ',', number + 1) - number - 1)
FROM (
SELECT string = #str1, id = 1
UNION ALL
SELECT #str2, 2
) t
CROSS JOIN [master].dbo.spt_values n
WHERE [type] = 'p'
AND number <= LEN(',' + t.string + ',') - 1
AND SUBSTRING(',' + t.string + ',', number, 1) = ','
)
SELECT tm FROM cte2 WHERE id = 2
EXCEPT
SELECT tm FROM cte2 WHERE id = 1
Can I put a WHILE loop inside WHERE clause? I have a stored procedure where I'm trying to put in text searching capability. I have it working for an exact match like this:
AND (#exactString = ''
OR (CHARINDEX(#exactString, [Short Description]) > 0
OR CHARINDEX(#exactString, [Description]) > 0
OR CHARINDEX(#exactString, [Manufacturer]) > 0))
Next I'm trying to do a "any word" match and an "all words" match. I can get the search string I want to search for with the following WHILE loop:
DECLARE #searchString varchar(max)
DECLARE #endIndex int
SET #allString = LTRIM(RTRIM(#allString))
WHILE LEN(#allString) > 0
BEGIN
SET #endIndex = CHARINDEX(' ', #allString) > 0
IF #endIndex > 0
BEGIN
SET #searchString = LEFT(#allString, #endIndex)
SET #allString = LTRIM(RTRIM(RIGHT(#allString, #endIndex)))
END
ELSE
BEGIN
SET #searchString = #allString
SET #allString = ''
END
END
Now I want to use the #searchString variable like I used #exactString above. Is there a way to do this inside my loop or is there some other technique I'm missing that would work here?
Thanks for your help,
Dan
I have used a table value function to perform this task using a query such as the following:
SELECT I.*
FROM #info AS I
INNER JOIN dbo.funcSplitToTable( ' ', #allString ) AS S
ON I.[Manufacturer] LIKE '%' + S.result + '%'
OR I.[Description] LIKE '%' + S.result + '%'
OR I.[Short Description] LIKE '%' + S.result + '%'
This table value function is defined as follows:
CREATE FUNCTION dbo.funcSplitToTable
/*
Split a string into parts base on a separation character to produce
a table that has one column containing the results of the split.
EXAMPLE:
SELECT * FROM dbo.funcSplitToTable( '~', 'MAINT~12221~10001~10/25/2004~CANCELLED~1' )
SELECT * FROM dbo.funcSplitToTable( '~', '' )
SELECT * FROM dbo.funcSplitToTable( '~', NULL )
SELECT * FROM dbo.funcSplitToTable( NULL, 'MAINT~12221~10001~10/25/2004~CANCELLED~1' )
SELECT * FROM dbo.funcSplitToTable( '', 'MAINT~12221~10001~10/25/2004~CANCELLED~1' )
RETURN:
Table with one column containing resulting strings.
*/
(
#strSearch AS varchar(255) -- String to search for.
,#strText AS varchar(MAX ) -- Text to search for string.
)
RETURNS #tblResult TABLE (
result varchar(MAX) NOT NULL
)
WITH SCHEMABINDING
AS
BEGIN
DECLARE #iLastPos int
DECLARE #iPos int
DECLARE #strResult varchar(MAX)
IF #strText IS NULL RETURN ;
IF #strSearch IS NULL SET #strSearch = '' ;
SET #strResult = NULL ;
SET #iLastPos = 1 ;
SET #iPos = CHARINDEX( #strSearch, #strText ) ;
WHILE #iPos > 0
BEGIN
IF (#iPos - #iLastPos) > 0
INSERT INTO #tblResult
SELECT SUBSTRING( #strText, #iLastPos, (#iPos - #iLastPos) ) AS result
;
SET #iLastPos = #iPos + 1 ;
SET #iPos = CHARINDEX( #strSearch, #strText, #iLastPos ) ;
END
IF (1 + LEN(#strText) - #iLastPos) > 0
INSERT INTO #tblResult
SELECT SUBSTRING( #strText, #iLastPos, (1 + LEN(#strText) - #iLastPos) ) AS result
;
RETURN ;
END
I got a great answer from Michael Erickson that totally works for the "any" search. For the "all" search. I built up an sql string with the entire query. The "all" search section is here:
IF LEN(#allString) > 0
BEGIN
DECLARE #searchString varchar(max)
DECLARE #endIndex int
DECLARE #isFirstString bit
SET #isFirstString = 0
SET #allString = LTRIM(RTRIM(#allString))
WHILE LEN(#allString) > 0
BEGIN
SET #endIndex = CHARINDEX(' ', #allString)
IF #endIndex > 0
BEGIN
SET #searchString = LTRIM(RTRIM(LEFT(#allString, #endIndex)))
SET #allString = LTRIM(RTRIM(RIGHT(#allString, LEN(#allString) - #endIndex)))
END
ELSE
BEGIN
SET #searchString = #allString
SET #allString = ''
END
SET #sql = #sql + ' AND ((CHARINDEX(''' + cast(#searchString as varchar(max)) + ''', [Short Description]) > 0
OR CHARINDEX(''' + cast(#searchString as varchar(max)) + ''', [Description]) > 0
OR CHARINDEX(''' + cast(#searchString as varchar(max)) + ''', [Manufacturer]) > 0))'
END
END
EXEC (#sql)
Thanks again,
Dan
I have a string:
#TempCol = sigma_x1,sigma_x2,...,sigma_xd,XX,YY,ZZ
I want to get a substring to get the string sigma_x1,sigma_x2,...,sigma_xd.
d is a variable, so it can be 1, 3, 20, ..., etc.
I know the value of d, but what I don't know is how to get the substring of the original string with d terms.
I tried this:
#L = ''
SET #ColumnNo = 0
WHILE #ColumnNo <= #d
BEGIN
SET #L = #L + ' ' + SUBSTRING(#TempCol, 1, CHARINDEX(',',#TempCol)-1 )
SET #TempCol = REPLACE ( #TempCol, LTRIM(RTRIM(#L) ) ,'')
Set #ColumnNo = #ColumnNo + 1
PRINT #L
END
but I do not know how to get the expected result.
DECLARE #TempCol varchar(max), #d int, #p int, #Result varchar(max);
SET #TempCol = 'item1,item2,itemA,itemB,item#,item$';
SET #d = 3;
SET #p = 1;
WHILE #d > 0 AND #p > 0 BEGIN
SET #p = CHARINDEX(',', #TempCol, #p);
IF #p > 0 SET #p = #p + 1;
SET #d = #d - 1;
END;
IF #p = 0
SET #Result = #TempCol
ELSE
SET #Result = SUBSTRING(#TempCol, 1, #p - 2);
SELECT #Result;
Basically, the loop just searches for the final position to cut at. The substring is extracted after the loop.
If you specify too large #d then the result will simply be all of #TempCol, otherwise you get the desired number of items.
What you need is a split function (shown at the bottom).
With SplitItems As
(
Select Position, Value
, Row_Number() Over ( Order By Position ) As ItemNum
From dbo.udf_Split( #TempCol, ',' )
)
Select Value
From SplitItems
Where ItemNum <= #d
If you want the assembled string up to a given point you would simply do:
With SplitItems As
(
Select Position, Value
, Row_Number() Over ( Order By Position ) As ItemNum
From dbo.udf_Split( #TempCol, ',' )
)
Select ',' + Value
From SplitItems
Where ItemNum <= #d
Order By ItemNum
For Xml Path('')
Split function:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Create FUNCTION [dbo].[udf_Split]
(
#DelimitedList nvarchar(max)
, #Delimiter nvarchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(#DelimitedList, Len(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
+ #DelimitedList
+ Case When Right(#DelimitedList, Len(#Delimiter)) <> #Delimiter Then #Delimiter Else '' End
As List
, Len(#Delimiter) As DelimiterLen
)
, Numbers As
(
Select TOP( Coalesce(DataLength(#DelimitedList)/2,0) ) Row_Number() Over ( Order By c1.object_id ) As Value
From sys.columns As c1
Cross Join sys.columns As c2
)
Select CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(#Delimiter, CL.list, N.Value + 1)
- ( CharIndex(#Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value <= DataLength(CL.List) / 2
And Substring(CL.List, N.Value, CL.DelimiterLen) = #Delimiter
)