Concatenate table of two columns into a single string with 2 delimiters - tsql

Let's say I have a table with two columns:
create table #counties(
state varchar(max),
name varchar(max)
)
insert #counties(state,name) values('Alabama','Autauga')
insert #counties(state,name) values('Alabama','Baldwin')
insert #counties(state,name) values('Texas','Adams')
insert #counties(state,name) values('Texas','Houston')
insert #counties(state,name) values('Wisconsin','Adair')
insert #counties(state,name) values('Wisconsin','Wood')
I'd like the result to be a single string like this:
Autauga;Baldwin^^Adams;Houston^^Adair;Wood
For each change in the first column, I'd like the ^^ delimiter, and for each change in the second column, I'd like the ; delimiter.
I'm aware of the coalesce function but I can't figure out how to use it over two columns, not just one:
declare #c varchar(max)
select #c = COALESCE(#c + ';', '') + name from #counties order by state, name
select #c
This is just for a data load but I'd still like to learn how to do it elegantly with just a select, and hopefully without a cursor loop.

Try it like this
With NamesPerState AS
(
SELECT c1.[state]
,STUFF(
(
SELECT ';' + c2.[name]
FROM #counties AS c2
WHERE c1.[state]=c2.[state]
ORDER BY c2.[name]
FOR XML PATH('')
),1,1,'') AS names
FROM #counties AS c1
GROUP BY c1.[state]
)
SELECT STUFF
(
(
SELECT '^^' + names
FROM NamesPerState
ORDER BY [state]
FOR XML PATH('')
),1,2,''
)
UPDATE
Just to show you, how easy this was with a clean and modern approach:
SELECT [state]
,
(
SELECT c2.[name]
FROM #counties AS c2
WHERE c1.[state]=c2.[state]
ORDER BY c2.[name]
FOR XML RAW('county'),TYPE
)
FROM #counties AS c1
GROUP BY c1.[state]
FOR XML RAW('state'),ROOT('counties')
The result
<counties>
<state state="Alabama">
<county name="Autauga" />
<county name="Baldwin" />
</state>
<state state="Texas">
<county name="Adams" />
<county name="Houston" />
</state>
<state state="Wisconsin">
<county name="Adair" />
<county name="Wood" />
</state>
</counties>
UPDATE 2 Just for fun: the minimal output
SELECT [state] AS [*]
,
(
SELECT c2.[name] AS [*]
FROM #counties AS c2
WHERE c1.[state]=c2.[state]
ORDER BY c2.[name]
FOR XML PATH('c'),TYPE
)
FROM #counties AS c1
GROUP BY c1.[state]
FOR XML PATH('s'),ROOT('counties')
The result
<counties>
<s>Alabama<c>Autauga</c><c>Baldwin</c></s>
<s>Texas<c>Adams</c><c>Houston</c></s>
<s>Wisconsin<c>Adair</c><c>Wood</c></s>
</counties>
And this would get your list back:
DECLARE #x XML=
'<counties>
<s>Alabama<c>Autauga</c><c>Baldwin</c></s>
<s>Texas<c>Adams</c><c>Houston</c></s>
<s>Wisconsin<c>Adair</c><c>Wood</c></s>
</counties>';
SELECT s.value('(text())[1]','nvarchar(max)') AS [state]
,c.value('.','nvarchar(max)') AS [name]
FROM #x.nodes('/counties/s') AS A(s)
OUTER APPLY s.nodes('c') AS B(c)

Related

tSQL transpose / pivot table dynamicly with multiple columns

Looking to pivot/transpose with tsql (or something else)? on a table with multiple rows per item number, one row per Code (unit of measure).
It would have to be dynamic as there could be lot of different unit of measure codes per item.
Current data table:
select [Item No_], Code, [Qty_ per Unit of Measure], Weight, Cubage
from [mycompany$Item Unit of Measure]
where [Item No_] in ('007967','007968')
Desired output would be:
Addiotional info
We have a table that holds all the possible Unit of Measure codes that perhaps could be used in the final code?
select
Code
from [mycompany$Unit of Measure]
How to acchieve this, and what would the SQL code look like?
Suggested solution from #Larnu :
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(Code)
FROM [mycompany$Unit of Measure]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query =
'SELECT *
FROM
(
SELECT
o.[Item No_],
p.Code,
o.Weight
FROM [mycompany$Item Unit of Measure] AS o
INNER JOIN [mycompany$Unit of Measure] AS p ON o.Code = p.Code
) AS t
PIVOT
(
MAX(Weight)
FOR Code IN( ' + #cols + ' )' +
' ) AS p ; ';
execute(#query);
However this only give max value for Weight and not Cubage. Also it doesn't meet the desired end results as column heads are not tagged with CODE.Cubage, CODE.Weight etc. (PALLET.Weight, PALLET.Cubage)
Screenshot of results with above code:

Xml Extract in DB2

Below have the xml framing fields,
**Xml_value=
<docs><doc_id>1234</doc_id>
<reci><reci_code>ss</reci_code>
</reci></docs>**
Select doc.value('doc_id[1]',varchar(8)')doc_id,
Reci.value('reci_code'[1],char(8)')reci_code
From xml_value.nodes('docs/doc')docs(doc)
cross apply docs.nodes('reci/reci')reci'(reci')
Output:
doc_id reci_code
1234 ss
The above mentioned query is for SQL server.
need to extract all XML fields into a separate columns as like above output statement in db2.
How to achieve this in db 2.
can you help on this.
Try this:
/*
WITH MYTABLE AS
(
SELECT T.*
FROM
(
VALUES XMLPARSE
(
DOCUMENT '
<docs>
<doc_id>1234</doc_id>
<reci>
<reci_code>ss</reci_code>
</reci>
</docs>
'
)
) T(X)
)
*/
SELECT V.DOC_ID, V.RECI_CODE
FROM
MYTABLE T
, XMLTABLE
(
'$D/docs/reci' PASSING T.X AS "D"
COLUMNS
DOC_ID INT PATH '../doc_id'
, RECI_CODE VARCHAR(10) PATH 'reci_code'
) V;
|DOC_ID |RECI_CODE |
|----------|----------|
|1234 |ss |

Db2 XML logics framing in select statement

In db2,
<row><element name>January</element name>
<error code>1010<\error code></row>
Need to frame as like this in db2 select statement.
In SQL server will handle this with the help of XML path()
Try this:
SELECT T.*
FROM
(
VALUES
'
<row>
<element_name>January</element_name>
<error_code>1010</error_code>
</row>
'
,
'
<row>
<element_name>February</element_name>
<error_code>2020</error_code>
</row>
'
) D(X)
, XMLTABLE
(
'$X/row' PASSING XMLPARSE (DOCUMENT D.X) AS "X"
COLUMNS
ENAME VARCHAR(10) PATH 'element_name'
, ERROR INT PATH 'error_code'
) T
which produces this output
ENAME ERROR
-------- -----
January 1010
February 2020

Get characters before underscore and separated by comma from a string in SQL Server 2008

I tried this query
DECLARE #AdvancedSearchSelectedDropdownName TABLE (
SelectedIds VARCHAR(2048),
AdvanceSearchOptionTypeId INT
)
INSERT INTO #AdvancedSearchSelectedDropdownName
VALUES ('4_0,5_1,6_2,7_3', 23),
('62_3', 21), ('2_4', 23)
DECLARE #selectedIds VARCHAR(MAX) = '';
SELECT #selectedIds +=
CASE WHEN SelectedIds IS NULL
THEN #selectedIds + ISNULL(SelectedIds + ',', '')
WHEN SelectedIds IS NOT NULL
THEN SUBSTRING(SelectedIds, 0, CHARINDEX('_', SelectedIds, 0)) + ','
END
FROM #AdvancedSearchSelectedDropdownName WHERE advanceSearchOptionTypeId = 23
SELECT #selectedIds
Current output: 4,2
Required output: 4,5,6,7,2
We may have n number of comma separated values in the SelectedIds column.
You might go this route:
WITH Casted AS
(
SELECT *
,CAST('<x><y>' + REPLACE(REPLACE(SelectedIds,'_','</y><y>'),',','</y></x><x><y>') + '</y></x>' AS XML) SplittedToXml
FROM #AdvancedSearchSelectedDropdownName
)
SELECT *
FROM Casted;
This will return your data in this form:
<x>
<y>4</y>
<y>0</y>
</x>
<x>
<y>5</y>
<y>1</y>
</x>
<x>
<y>6</y>
<y>2</y>
</x>
<x>
<y>7</y>
<y>3</y>
</x>
Now we can grab all the x and just the first y:
WITH Casted AS
(
SELECT *
,CAST('<x><y>' + REPLACE(REPLACE(SelectedIds,'_','</y><y>'),',','</y></x><x><y>') + '</y></x>' AS XML) SplittedToXml
FROM #AdvancedSearchSelectedDropdownName
)
SELECT Casted.AdvanceSearchOptionTypeId AS TypeId
,x.value('y[1]/text()[1]','int') AS IdValue
FROM Casted
CROSS APPLY SplittedToXml.nodes('/x') A(x);
The result:
TypeId IdValue
23 4
23 5
23 6
23 7
21 62
23 2
Hint: Do not store comma delimited values!
It is a very bad idea to store your data in this format. You can use a generic format like my XML to store this or a structure of related side tables. But such construction tend to turn out as a real pain in the neck...
After a little re-think. Perhaps something a little more straightforward.
Now, if you have a limited number of _N
Example
;with cte as (
Select *
,RN = Row_Number() over(Order by (Select NULL))
From #AdvancedSearchSelectedDropdownName A
)
Select AdvanceSearchOptionTypeId
,IDs = replace(
replace(
replace(
replace(
replace(
stuff((Select ',' +SelectedIds From cte Where AdvanceSearchOptionTypeId=A.AdvanceSearchOptionTypeId Order by RN For XML Path ('')),1,1,'')
,'_0','')
,'_1','')
,'_2','')
,'_3','')
,'_4','')
From cte A
Group By AdvanceSearchOptionTypeId
Returns
AdvanceSearchOptionTypeId IDs
21 62
23 4,5,6,7,2
If interested in a helper function.
Tired of extracting strings (left, right, charindex, patindex, ...) I modified s split/parse function to accept TWO non-like delimiters. In this case a , and _.
Example
;with cte as (
Select A.AdvanceSearchOptionTypeId
,B.*
,RN = Row_Number() over(Order by (Select NULL))
From #AdvancedSearchSelectedDropdownName A
Cross Apply [dbo].[tvf-Str-Extract](','+A.SelectedIds,',','_') B
)
Select AdvanceSearchOptionTypeId
,IDs = stuff((Select ',' +RetVal From cte Where AdvanceSearchOptionTypeId=A.AdvanceSearchOptionTypeId Order by RN,RetVal For XML Path ('')),1,1,'')
From cte A
Group By AdvanceSearchOptionTypeId
Returns
AdvanceSearchOptionTypeId IDs
21 62
23 4,5,6,7,2
The TVF if Interested
CREATE FUNCTION [dbo].[tvf-Str-Extract] (#String varchar(max),#Delimiter1 varchar(100),#Delimiter2 varchar(100))
Returns Table
As
Return (
with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A ),
cte3(N) As (Select 1 Union All Select t.N+DataLength(#Delimiter1) From cte2 t Where Substring(#String,t.N,DataLength(#Delimiter1)) = #Delimiter1),
cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(#Delimiter1,#String,s.N),0)-S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By N)
,RetPos = N
,RetVal = left(RetVal,charindex(#Delimiter2,RetVal)-1)
From (
Select *,RetVal = Substring(#String, N, L)
From cte4
) A
Where charindex(#Delimiter2,RetVal)>1
)
/*
Max Length of String 1MM characters
Declare #String varchar(max) = 'Dear [[FirstName]] [[LastName]], ...'
Select * From [dbo].[tvf-Str-Extract] (#String,'[[',']]')
*/
Disclaimer.As per first Normal form, you should not store multiple values in a single cell. I would suggest you to avoid storing this way.
Still the approach would be: Create a UDF function which separates comma separated list into a table valued variable. Below code I have not tested. but, it gives idea on how to approach this problem.
Refer to CSV to table approaches
Declare #selectedIds varchar(max) = '';
SET #selectedIds = SELECT STUFF
(SELECT ','+ (SUBSTRING(c.value, 0, CHARINDEX('_', c.value, 0))
FROM #AdvancedSearchSelectedDropdownName AS tv
CROSS APPLY dbo.udfForCSVToList(SelectedIds) AS c
WHERE advanceSearchOptionTypeId = 23
FOR XML PATH('')),1,2,'');
SELECT #selectedIds

Display formatted JSON in SSRS report

I have a table where one field is JSON string.
"CX.UW.001": "03/08/2017", "CX.UW.001.AUDIT": "admin",
I want to produce an SSRS report where it appears in readable format like:
CX.UW.001: 03/08/2017
CX.UW.001.AUDIT: admin
Is it possible?
If you are looking for multiple records, just about any parse/split function will do, or you can use a simple CROSS APPLY in concert with a little XML
Declare #YourTable table (ID int, JSON varchar(max))
Insert Into #YourTable values
(1,'"CX.UW.001": "03/08/2017", "CX.UW.001.AUDIT": "admin"')
Select A.ID
,DisplayAs = replace(B.RetVal,'"','')
From #YourTable A
Cross Apply (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(A.JSON,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as X
Cross Apply x.nodes('x') AS B(i)
) B
Returns
ID DisplayAs
1 CX.UW.001: 03/08/2017
1 CX.UW.001.AUDIT: admin
Or If you want the string to wrap
Select A.ID
,DisplayAs = replace(replace(JSON,',',char(13)),'"','')
From #YourTable A
Returns
1 CX.UW.001: 03/08/2017
CX.UW.001.AUDIT: admin
Right click that field, choose expression, locate Text from Common Functions category, use Replace function, should be the syntax like :
Replace (Fields!Yours.Value.Value,"""","")
Or in TSQL:
Select Replace(JSON_COLUMN,'"','')
From table