SQL Server Populate XML Variable from VARCHAR rows - sql

I have a single column table in SQL Server.
This column hold each row of an XML document.
Sample table :
Column
---------------
Row1: <ROOT>
Row2: <Name>name1</Name>
Row3: </ROOT>
Column data type is nvarchar(max)
I want to do:
DECLARE #RES_XML XML
SET #XML = Set from table above
How can I sum up all rows of the table above and populate #RES_XML?
Note: when concatenated; all data in the table exceeds nvarchar(max) limit.

In general, easiest way to concatenate values into variable is
declare #res nvarchar(max)
select #res = isnull(#res, '') + [Column]
from <table above>
select cast(#res as xml)
Of course, order of concatenation in this query is not defined (but there is a trick to check if order is maintained)

I am as surprised by your exceeding the limit of nvarchar(max) columns as Marc is, but maybe you have column limit settings in your database set-up that you can't change. Here's how I would do it, and I'm assuming there is some column that let's you order the tags in the appropriate order, you cannot rely on the order in which the database decided to store and retrieve the rows.
declare #t table (
id int,
x nvarchar(max)
)
insert into #t
select 1, '<ROOT>' union all
select 2, '<Name>name1</Name>' union all
select 3, '</ROOT>'
DECLARE #RES_XML XML
select #RES_XML = cast((
select x
from #t
order by id
for xml path(''), type
).value('.', 'nvarchar(max)') as xml)

Related

Replace columns separated by string with id from another table - SQL Server

I have following 2 tables in SQL Server
Category table:
Category
--------------------------
Delivery;Gauges;Book;Table
Category id:
id name
-----------------
13183 Delivery
88781 Gauges
88782 Book
12512 Table
Intended result is to have category table replaced with category id, as:
Category
-----------------------
13183;88781;88782;12512
I approached this by first separating category columns into separate columns using :
ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
and so on. Then used left join and replace on each new column. Isn't there an easier way to do this? I searched on the net and stackoverflow but can't seem to find anything similar.
You can try to make a function to split your string value by a character.
CREATE FUNCTION Split_fun
( #Words nvarchar(MAX)
, #splitStr varchar(50)
)
RETURNS #Result_Table TABLE
(
[word] nvarchar(max) NULL
)
BEGIN
Declare #TempStr nvarchar(MAX)
WHILE (CHARINDEX(#splitStr,#Words)>0)
BEGIN
Set #TempStr=SUBSTRING(#Words,1,CHARINDEX(#splitStr,#Words)-1)
Insert into #Result_Table (word) Values (#TempStr)
Set #Words = REPLACE(#Words,#TempStr+#splitStr,'')
END/*End While*/
IF(LEN(RTRIM(LTRIM(#Words)))>0 And CHARINDEX(#splitStr,RTRIM(LTRIM(#Words)))=0)
Begin
Set #TempStr=#Words
Insert into #Result_Table (word) Values (#TempStr)
End
RETURN
END
you can use this function to make a result set by ';'.
do self-join with Category id table.
final you can use FOR XML connect all string by ; to get your expectation result.
;with cte as (
SELECT id
FROM T CROSS APPLY Split_fun(Category,';') v
JOIN T1 on v.word = t1.Category
)
select STUFF((
select distinct ';'+ cast(id as varchar(10))
FROM cte
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
sqlfiddle

How to Convert XML Rows to Columns

I have seen a few answers on how to convert rows to columns but they are rather specific to the questions and are difficult for me to transpose into my own solution.
The data starts out as a varchar, but I convert it to XML because I thought it would be easier to convert it to columns that way.
-- get xml
DECLARE #x XML
SET #x = '<ul><li>Gas grill rotisserie</li><li>Fits the Genesis E-300 gas grill</li><li>Fits the Genesis S-300 gas grill</li><li>Includes a heavy-duty electric motor</li><li>Counterbalance for smooth turning and less motor wear</li></ul>'
SELECT x.r.value('node()[1]','varchar(200)')
FROM #x.nodes('/ul/li') AS x(r)
This returns a results like the following; however, I now need to convert each row into a column.
I have tried variations using pivot and dynamic SQL, but haven't gotten very far. How can I convert each row to a column (when the number of rows will be unknown).
Ref. Convert Rows to columns using 'Pivot' in SQL Server
Ref. How to convert row values to columns with dynamic columns count?
I was SOreadytohelp myself this time :) ... The following query receives the HTML, converts it to XML, defines the column names and writes the dynamic SQL before executing it.
The end result is:
DECLARE #x XML,
#limit int = 4,
#ItemId NVARCHAR(10) = '11158',
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
-- get xml
SELECT #x = '<ul><li>Gas grill rotisserie</li><li>Fits the Genesis E-300 gas grill</li><li>Fits the Genesis S-300 gas grill</li><li>Includes a heavy-duty electric motor</li><li>Counterbalance for smooth turning and less motor wear</li></ul>'
-- convert rows to columns
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(name)
from
(
SELECT top (#limit)
bar.value('local-name(.)','VARCHAR(12)') + cast(row_number() over(order by bar.value('./.','VARCHAR(10)') asc) as varchar(10)) as name,
bar.value('./.','VARCHAR(255)') as value
FROM
#x.nodes('/ul/*') AS foo(bar)
) d
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
-- create dynamic sql
set #query = '
-- get xml
DECLARE #x XML
SELECT #x = ''<ul><li>Gas grill rotisserie</li><li>Fits the Genesis E-300 gas grill</li><li>Fits the Genesis S-300 gas grill</li><li>Includes a heavy-duty electric motor</li><li>Counterbalance for smooth turning and less motor wear</li></ul>''
SELECT ' + #cols + '
from
(
SELECT
bar.value(''local-name(.)'',''VARCHAR(12)'') + cast(row_number() over(order by bar.value(''./.'',''VARCHAR(10)'') asc) as varchar(10)) as name,
bar.value(''./.'',''VARCHAR(255)'') as value
FROM
#x.nodes(''/ul/*'') AS foo(bar)
) x
pivot
(
max(value)
for name in (' + #cols + ')
) p '
execute sp_executesql #query;

How to concatenate all the rows of a single int column to get a single string?

Table: I have a database table table_1 in SQL Server 2012 with data as:
CREATE TABLE table_1
(
name nvarchar(128) not null,
state tinyint null,
state_desc nvarchar(60) null
);
INSERT INTO table_1
VALUES ('text1',1,'ONLINE'),
('text2',0,'ONLINE'),
('text3',0,'ONLINE'),
('text4',0,'ONLINE'),
('TEXTTE',0,'ONLINE'),
('TEXTTEXT',0,'ONLINE'),
('EXTTEXT',0,'ONLINE'),
('TEXTex_EX_Ext',0,'ONLINE'),
('TEXTex_TEX_Ext',0,'ONLINE'),
('TEXTTEXTText',0,'ONLINE'),
('Texttextext',0,'ONLINE'),
('TextTextext',0,'ONLINE'),
('TEXTER',1,'ONLINE');
I want to select all the values in column state and concatenate them to get a single string.
So desired result is a single row and a single column with data as:
1000000000001
What I have tried: Using substring but it skips first row (1) and gives 13 rows (000000000001 in each row) instead of just 1.
Select
substring(
(
Select ''+ST1.[state] AS [text()]
From dbo.table_1 ST1
For XML PATH ('')
), 2, 1000) [state]
From dbo.table_1 ST2;
Is there any other way to do this?
I will not know the number of rows and I want to keep the sequence while concatenating. (First row should be first digit, second row second digit, etc)
It doesn't matter if the first row goes rightmost or leftmost after concatenation, just it needs to be consistent and in sequence.
Your query is almost correct. You just do not need the part with substring. Also I suggest you to order rows while concatenating with for xml path. Do you have some ID column? I have slightly modified your query:
select result = (
Select ''+ST1.[state] AS [text()]
From dbo.table_1 ST1
For XML PATH ('')
)
--Try this query
SELECT replace([state],',','')
FROM(
SELECT stuff( (SELECT ',' + CONVERT(VARCHAR(1000), ST1.[state])
FROM table_1 ST1
FOR XML PATH(''), TYPE).value('.', 'varchar(max)')
,1,1,'')
AS [state]
)t
try using variable
see this
declare #Str varchar(1000)
set #Str = ''
update table_1
set #Str = #Str + cast(state as varchar)
select #Str

SQL Server: Convert single row to comma delimited (separated) format

As the title states, I need help in converting a single row of data E.g,
col1 col2 col3 <-- This are column names
value1 value2 value3
To something like
dataResult <-- this is the column name from running the procedure or call
value1,value2,value3
The requirements are that this call ( or rather procedure) needs to be able to accept the results of sql queries of any column length and is able to convert that row to a comma delimited string format. Been stuck at this for weeks any help would be greatly appreciated...
EDIT*
Assume the unique key is the first column. Also assume that only 1 row will be returned with each query. Multiple rows will never occur.
The idea is to convert that row to a comma separated string without having to select the column names manually (in a sense automatically convert the query results)
You might try it like this:
A declared table variable to mock-up as test table. Be aware of the NULL value in col2!
DECLARE #tbl TABLE(col1 VARCHAR(100),col2 VARCHAR(100),col3 VARCHAR(100));
INSERT INTO #tbl VALUES('test1',NULL,'test3');
--This is the query:
SELECT
STUFF(
(
SELECT ',' + elmt.value('.','nvarchar(max)')
FROM
(
SELECT
(
/*YOUR QUERY HERE*/
SELECT TOP 1 *
FROM #tbl
/*--------------------*/
FOR XML AUTO ,ELEMENTS XSINIL,TYPE
)
) AS A(t)
CROSS APPLY t.nodes('/*/*') AS B(elmt)
FOR XML PATH('')
),1,1,'')
FOR XML AUTO will return each row as XML with all the values within attributes. But this would omit NULL values. Your returned string would not inlcude the full count of values in this case. Stating ELEMENT XSINIL forces the engine to include NULL values into the XML. This CROSS APPLY t.nodes('/*/*') will return all the elements as derived table and the rest is re-conactenation.
See the double comma in the middle! This is the NULL value of col2
test1,,test3
ATTENTION: You must be aware, that the whole approach will break, if there is a comma part of a (string) column...
Hint
Better was a solution with XML or JSON. Comma separated values are outdated...
Applay the next Approach:-
Use For Xml to sperate comma,
Get Columns Names Via using INFORMATION_SCHEMA.COLUMNS.
According to your need, select TOP (1) for getting First
Row.
Demo:-
Create database MyTestDB
go
Use MyTestDB
go
Create table Table1 ( col1 varchar(10), col2 varchar(10),col3 varchar(10))
go
insert into Table1 values ('Value1','Value2','Value3')
insert into Table1 values ('Value11','Value12','Value13')
insert into Table1 values ('Value21','Value22','Value23')
go
Declare #Values nVarchar(400),
#TableName nvarchar (100),
#Query nvarchar(max)
Set #TableName = 'Table1'
Select #Values = Stuff(
(
Select '+'','' + ' + C.COLUMN_NAME
From INFORMATION_SCHEMA.COLUMNS As C
Where C.TABLE_SCHEMA = T.TABLE_SCHEMA
And C.TABLE_NAME = T.TABLE_NAME
Order By C.ORDINAL_POSITION
For Xml Path('')
), 1, 2, '')
From INFORMATION_SCHEMA.TABLES As T
where TABLE_NAME = #TableName
select #Values = right(#Values,len(#Values)-4)
select #Query = 'select top(1)' + #Values + ' from ' + #TableName
exec sp_executeSQL #Query
Result:-

Cast column name to specific character during Pivot table creation - SQL

I have a table test which has a column Label. It has Data which is longers than 50 characters in length.
When I create a pivot table from the 'test' table , it uses those long charactered data as column name.
My requirement is to user cast function to limit the column names to say 26 characters.
I use the below script , but it doesnt work as desired.
create table V_Test as
select * from
(select * from Test) x
pivot (sum(Average) for Label in (
S03_CreatePlansdadsada,
S03_CreatePlan_T01_NavigateTosdsadsaded,
S03_CreatePlan_T03_abcdefgmanagementsdasda,
S03_CreatePlan_T16_SetStatusToOngoingasdasda,
S03_CreatePlan_T17_Ldsdssdadsadas
)
) p
Thanks in advance for the help
Since the label names are hard-coded anyway, then using aliases for them should be fine.
On MS SQL Server it would look something like this.
Just using a variable table and a temporary table for the purpose of demonstration.
DECLARE #Test TABLE (PlanID int, Label varchar(50), Average int);
insert into #Test values
(1,'S03_CreatePlansdadsada',10),
(1,'S03_CreatePlan_T01_NavigateTosdsadsaded',20),
(1,'S03_CreatePlan_T03_abcdefgmanagementsdasda',30),
(1,'S03_CreatePlan_T16_SetStatusToOngoingasdasda',40),
(1,'S03_CreatePlan_T17_Ldsdssdadsadas',50),
(1,'S03_CreatePlansdadsada',60),
(1,'S03_CreatePlan_T01_NavigateTosdsadsaded',70),
(1,'S03_CreatePlan_T03_abcdefgmanagementsdasda',80),
(1,'S03_CreatePlan_T16_SetStatusToOngoingasdasda',90),
(1,'S03_CreatePlan_T17_Ldsdssdadsadas',100);
IF OBJECT_ID('tempdb..#tmpTest') IS NOT NULL DROP TABLE #tmpTest;
select
PlanID,
S03_CreatePlansdadsada as CreatePlans,
S03_CreatePlan_T01_NavigateTosdsadsaded as T01_NavigateTo,
S03_CreatePlan_T03_abcdefgmanagementsdasda as T03_managements,
S03_CreatePlan_T16_SetStatusToOngoingasdasda as T16_SetStatusToOng,
S03_CreatePlan_T17_Ldsdssdadsadas as T17_Lsd
into #tmpTest
from #Test
pivot (sum(Average) for Label in (
S03_CreatePlansdadsada,
S03_CreatePlan_T01_NavigateTosdsadsaded,
S03_CreatePlan_T03_abcdefgmanagementsdasda,
S03_CreatePlan_T16_SetStatusToOngoingasdasda,
S03_CreatePlan_T17_Ldsdssdadsadas
)
) p;
select * from #tmpTest;
This would return the following results:
PlanID CreatePlans T01_NavigateTo T03_managements T16_SetStatusToOng T17_Lsd
1 70 90 110 130 150
The dynamic way is a bit more complicated, since the SQL needs to be constructed so it can be executed. (beware of code injection)
IF OBJECT_ID('tempdb..#tmpTestData') IS NOT NULL DROP TABLE #tmpTestData;
CREATE TABLE #tmpTestData (PlanID int, Label varchar(50), Average int);
insert into #tmpTestData values
(1,'S03_CreatePlansdadsada',10),
(1,'S03_CreatePlan_T01_NavigateTosdsadsaded',20),
(1,'S03_CreatePlan_T03_abcdefgmanagementsdasda',30),
(1,'S03_CreatePlan_T16_SetStatusToOngoingasdasda',40),
(1,'S03_CreatePlan_T17_Ldsdssdadsadas',50),
(1,'S03_CreatePlansdadsada',60),
(1,'S03_CreatePlan_T01_NavigateTosdsadsaded',70),
(1,'S03_CreatePlan_T03_abcdefgmanagementsdasda',80),
(1,'S03_CreatePlan_T16_SetStatusToOngoingasdasda',90),
(1,'S03_CreatePlan_T17_Ldsdssdadsadas',100);
declare #Columns nvarchar(max);
declare #AliasedColumns nvarchar(max);
set #Columns = STUFF((SELECT ', ' + QUOTENAME(Label) FROM #tmpTestData GROUP BY Label FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(max)') ,1,1,'');
set #AliasedColumns = STUFF((SELECT ', ' + QUOTENAME(Label) +' as '+ QUOTENAME(substring(Label,5,14)) FROM #tmpTestData GROUP BY Label FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(max)') ,1,1,'');
declare #SQL nvarchar(max);
IF OBJECT_ID('tempdb..##tmpPivotTest') IS NOT NULL DROP TABLE ##tmpPivotTest;
set #SQL = 'select PlanID, '+ #AliasedColumns + '
into ##tmpPivotTest
from #tmpTestData pivot (sum(Average) for Label in ('+ #Columns +')) p';
--select #SQL;
exec (#SQL);
select * from ##tmpPivotTest;
IF OBJECT_ID('tempdb..##tmpPivotTest') IS NOT NULL DROP TABLE ##tmpPivotTest;
For an Oracle database it would look something like this:
CREATE TABLE V_Test AS
SELECT * FROM (select Label, Average from Test)
PIVOT (
SUM(Average) AS sum_average FOR (Label) IN (
'S03_CreatePlansdadsada' as CreatePlans,
'S03_CreatePlan_T01_NavigateTosdsadsaded' as T01_NavigateTo,
'S03_CreatePlan_T03_abcdefgmanagementsdasda' as T03_Managements,
'S03_CreatePlan_T16_SetStatusToOngoingasdasda' as T16_SetStatusToOng,
'S03_CreatePlan_T17_Ldsdssdadsadas' as T17_Lsd
)
) p;