How to do OrderBY on XML column in SQL SERVER 2008 - sql-server

I am creating a comma separated value of columns specified in the dbName attribute of the XML below. Now I want to concatenate those columns on the basis of the Position attribute.
DECLARE #varXML AS XML =
'<gridFormat>
<column property="FacilityInternalID" dbName="Pname" HeaderText="TAT Health" IsVisible="1" Position="1" />
<column property="FacilityInternalID" dbName="Priority" HeaderText="Priority" IsVisible="1" Position="2" />
<column property="FacilityInternalID" dbName="JobID" HeaderText="Job Number" IsVisible="1" Position="3" />
<column property="FacilityInternalID" dbName="Status" HeaderText="Status" IsVisible="1" Position="6" />
<column property="FacilityInternalID" dbName="name" HeaderText="Customer" IsVisible="1" Position="4" />
<column property="FacilityInternalID" dbName="sname" HeaderText="Facility " IsVisible="1" Position="5" />
</gridFormat>'
PRINT #varXML
This is the query by which I am generating CSV of columns. I have to use it as a select list.
SELECT #ColumnsToDisplay = LEFT(MyCsvList, LEN(MyCsvList) - 1)
FROM ( SELECT ( SELECT row.value('#property',
'varchar(200)') + ', ' AS [text()]
FROM #varXML.nodes('gridFormat/column')
AS d ( row )
FOR
XML PATH('')
) AS MyCsvList
) AS MyCsvListFinal
SET #SQL = 'SELECT ' + #ColumnsToDisplay
+ ' FROM JobListingDetails'
The result should be
select Pname,Priority,JobID,name,sname,status FROM JobListingDetails.
Please help.

This should work:
Note that you need to read the dbName attribute.
[EDIT] Updated to include order by. Basicaly you also read the Postion attribute from xml, then use that in the ORDER BY:
declare #ColumnsToDisplay varchar(max)
DECLARE #varXML AS XML
set #varxml =
'<gridFormat>
<column property="FacilityInternalID" dbName="Pname" HeaderText="TAT Health" IsVisible="1" Position="1" />
<column property="FacilityInternalID" dbName="Priority" HeaderText="Priority" IsVisible="1" Position="2" />
<column property="FacilityInternalID" dbName="JobID" HeaderText="Job Number" IsVisible="1" Position="3" />
<column property="FacilityInternalID" dbName="Status" HeaderText="Status" IsVisible="1" Position="6" />
<column property="FacilityInternalID" dbName="name" HeaderText="Customer" IsVisible="1" Position="4" />
<column property="FacilityInternalID" dbName="sname" HeaderText="Facility " IsVisible="1" Position="5" />
</gridFormat>'
SELECT #ColumnsToDisplay = COALESCE(#ColumnsToDisplay + ',', '') + dbName
FROM
(
SELECT row.value('#dbName','varchar(200)') AS dbName, row.value('#Position','int') as pos
FROM #varXML.nodes('gridFormat/column')
AS d ( row )
) csv
order by pos
select 'SELECT ' + #ColumnsToDisplay + ' FROM JobListingDetails'

See this article for a cool way to do string concatenation without using a cursor.
It's under the "Replacing the cursor with a SET based approach" section.

Related

SQL Server: column with XML data

I want to extract the value from the XML data to columns.
I tried using this:
DECLARE #xml XML
SELECT #xml = Data FROM synergy..XMLData
SELECT
xmlData.Col.value('(column/#value)[1]','varchar(255)') AS 'Artikelen',
xmlData.Col.value('(column/#value)[2]','varchar(255)') AS 'Batchnummer',
xmlData.Col.value('(column/#value)[3]','varchar(255)') AS 'Aantal'
FROM
#XML.nodes ('//table/rows/row/columns') xmldata(Col)
however I'm only getting titles, but no data.
This is what the XML looks like:
<?xml version="1.0" encoding="utf-16"?>
<table>
<id>{941D5F5A-156A-4F19-A3B0-111E9825707B}</id>
<rows>
<row>
<columns>
<column name="Artikelen" value="102535.A.M2" type="System.String" />
<column name="Batchnummer" value="19D1739/133" type="System.String" />
<column name="Aantal" value="8" type="System.Int32" />
<column name="Opmerkingen" value="te weinig" type="System.String" />
<column name="Selecteren" value="1" type="System.String" />
<column name="DefaultKey" value="1" type="System.Int32" />
</columns>
</row>
</rows>
<key>DefaultKey</key>
<total>0</total>
<AddOnKey>0</AddOnKey>
<data />
<parameters />
</table>
It is better to shred XML and get needed data by referring to the named attributes instead of their position.
SQL
DECLARE #xml XML = '
<table>
<id>{941D5F5A-156A-4F19-A3B0-111E9825707B}</id>
<rows>
<row>
<columns>
<column name="Artikelen" value="102535.A.M2" type="System.String"/>
<column name="Batchnummer" value="19D1739/133" type="System.String"/>
<column name="Aantal" value="8" type="System.Int32"/>
<column name="Opmerkingen" value="te weinig" type="System.String"/>
<column name="Selecteren" value="1" type="System.String"/>
<column name="DefaultKey" value="1" type="System.Int32"/>
</columns>
</row>
</rows>
<key>DefaultKey</key>
<total>0</total>
<AddOnKey>0</AddOnKey>
<data/>
<parameters/>
</table>';
SELECT col.value('(column[#name="Artikelen"]/#value)[1]','VARCHAR(255)') AS [Artikelen]
, col.value('(column[#name="Batchnummer"]/#value)[1]','VARCHAR(255)') AS [Batchnummer]
, col.value('(column[#name="Aantal"]/#value)[1]','INT') AS [Aantal]
FROM #XML.nodes ('/table/rows/row/columns') AS tab(col);
Output
+-------------+-------------+--------+
| Artikelen | Batchnummer | Aantal |
+-------------+-------------+--------+
| 102535.A.M2 | 19D1739/133 | 8 |
+-------------+-------------+--------+

SQL Server stored procedure - Table to XML object

I need to write a stored procedure that can create the following output from values from a SQL Server table:
<object>
<ValueList>
<Value value="1" text="Name" enabled="1" alias="Alias" />
<Value value="2" text="Name" enabled="1" alias="Alias" />
<Value value="3" ....
...
...
</ValueList>
</object>
I have tried to convert my table into XML using this procedure:
select TXT as [text]
from lit_geography
for XML raw ('value'), root('object')
Then I get the following
<object>
<value text="Midler tidig havn" />
<value text="Færgehavnsvej" />
<value text="Sydhavnsvej" />
<value text="Ø-Pladsen" />
<value text="Havnepladsen" />
</object>
TODO:
I am missing the parent node Valuelist
I need to append the following:
value="1", value="2" etc etc counting up for each value
enabled="1"
alias=""
For the incremental value, you can use Row_Number()
Example
Select (
Select [value] = row_number() over (Order by (select null))
,[text] = txt
,[enabled] = 1
,[alias] = 'Alias'
From lit_geography
For XML Raw('Value'),type
)
For XML Path('ValueList'),Root('object')
Returns
<object>
<ValueList>
<Value value="1" text="Midler tidig havn" enabled="1" alias="Alias" />
<Value value="2" text="Færgehavnsvej" enabled="1" alias="Alias" />
<Value value="3" text="Sydhavnsvej" enabled="1" alias="Alias" />
<Value value="4" text="Ø-Pladsen" enabled="1" alias="Alias" />
<Value value="5" text="Havnepladsen" enabled="1" alias="Alias" />
</ValueList>
</object>
EDIT - Requested UPDATE
Update lit_formfield set [Values] = (
Select (
Select [value] = row_number() over (Order by (select null))
,[text] = txt
,[enabled] = 1
,[alias] = 'Alias'
From lit_geography
For XML Raw('Value'),type
)
For XML Path('ValueList'),Root('object')
)
Where Label = 'listbox'

Convert XML to SQL Server 2008R2 table [duplicate]

This question already has answers here:
Import 'xml' into Sql Server
(5 answers)
Closed 5 years ago.
I want to import below XML file into SQL table. (SQL Server 2008R2)
<table>
<id>{72cbb5ab-dbb3-4de7-9010-5dd1192a1851}</id>
<rows>
<row>
<columns>
<column name="itemcode" value="0984-22-301" type="System.String" />
<column name="date" value="08-November-2017" type="System.DateTime" />
<column name="amount" value="10" type="System.Decimal" />
<column name="DefaultKey" value="1" type="System.Int32" />
</columns>
</row>
<row>
<columns>
<column name="itemcode" value="0984-33-101" type="System.String" />
<column name="date" value="08-November-2017" type="System.DateTime" />
<column name="amount" value="11" type="System.Decimal" />
<column name="DefaultKey" value="2" type="System.Int32" />
</columns>
</row>
</rows>
<key>DefaultKey</key>
<total>0</total>
<data />
<parameters />
</table>
It should look like a sql table with columns id, itemcode, date and amount.
How should my query look like?
Solved the question.
declare #xmltable table (data xml)
insert into #xmltable (data)
select DATA from [MyData]..myxml
SELECT
LineId = c.value('id[1]', 'nvarchar(max)'),
ColumnItemCode = l.value('(columns/column[#name="itemcode"]/#value)[1]', 'varchar(20)'),
ColumnDate = l.value('(columns/column[#name="date"]/#value)[1]', 'varchar(20)'),
ColumnAmount = l.value('(columns/column[#name="amount"]/#value)[1]', 'varchar(20)')
FROM
#xmltable x
CROSS APPLY data.nodes('table') t(c)
CROSS APPLY data.nodes('table/rows/row') b(l)
This resulted in:
LineId ColumnItemCode ColumnDate ColumnAmount
{72cbb5ab-dbb3-4de7-9010-5dd1192a1851} 0984-22-301 08-November-2017 10
{72cbb5ab-dbb3-4de7-9010-5dd1192a1851} 0984-33-101 08-November-2017 11
Thanks for your help.

Query Optimization while updating XML String in SQL Server?

I'm working with XML string shown below.
I have to update the XML string as follows:
If the XML string contains 1000 records or more, it kills the query
If the XML string contains < 1000 records, let it continue.
How can I do this?
example data
<root xmlns:json="http://james.newtonking.com/projects/json">
<row json:Array="true" RowNumber="1">
<Column json:Array="true" Name="Number" Value="1" />
<Column json:Array="true" Name="HourFrom" Value="13.2" />
<Column json:Array="true" Name="HourTo" Value="13.3" />
<Column json:Array="true" Name="Rate" Value="0.895" />
</row>
<row json:Array="true" RowNumber="2">
<Column json:Array="true" Name="Number" Value="1" />
<Column json:Array="true" Name="HourFrom" Value="13.3" />
<Column json:Array="true" Name="HourTo" Value="13.4" />
<Column json:Array="true" Name="Rate" Value="0.907" />
</row>
</root>
Temp table creation
CREATE TABLE #xmltable(
Id INT Identity (1,1) PRIMARY KEY CLUSTERED,
DataValue XML
);
CREATE PRIMARY XML INDEX indexratesheet ON #xmltable
(
DataValue
)
Inserting data into table
INSERT INTO (DataValue ) VALUES(TheXMLfromAbove)
updating the XML string in the table
DECLARE #i INT 1
WHILE(#i<=1000)
BEGIN
UPDATE #xmltable SET DataValue.modify('insert <Column Name="ValidationComments" Value="{sql:variable("#validationcomments")}"></Column>
into (/root/row[#i=sql:variable("#i")])[1]')
SET #i=#i+1
END
If you have to keep this with XML it should be much faster to shred the whole XML into a derivedTable and re-build it from scratch.
Try this:
CREATE TABLE #xmltable(
Id INT Identity (1,1) PRIMARY KEY CLUSTERED,
DataValue XML
);
CREATE PRIMARY XML INDEX indexratesheet ON #xmltable
(
DataValue
);
--Your test XML
INSERT INTO #xmltable (DataValue ) VALUES(N'<root xmlns:json="http://james.newtonking.com/projects/json">
<row json:Array="true" RowNumber="1">
<Column json:Array="true" Name="Number" Value="1" />
<Column json:Array="true" Name="HourFrom" Value="13.2" />
<Column json:Array="true" Name="HourTo" Value="13.3" />
<Column json:Array="true" Name="Rate" Value="0.895" />
</row>
<row json:Array="true" RowNumber="2">
<Column json:Array="true" Name="Number" Value="1" />
<Column json:Array="true" Name="HourFrom" Value="13.3" />
<Column json:Array="true" Name="HourTo" Value="13.4" />
<Column json:Array="true" Name="Rate" Value="0.907" />
</row>
</root>');
--The query to shred it
SELECT r.value(N'#RowNumber','int') AS RowNumber
,r.value(N'(Column[#Name="Number"]/#Value)[1]','int') AS Number
,r.value(N'(Column[#Name="HourFrom"]/#Value)[1]','decimal(10,4)') AS HourFrom
,r.value(N'(Column[#Name="HourTo"]/#Value)[1]','decimal(10,4)') AS HourTo
,r.value(N'(Column[#Name="Rate"]/#Value)[1]','decimal(10,4)') AS Rate
INTO #derivedTable
FROM #xmltable AS t
CROSS APPLY t.DataValue.nodes(N'/root/row') AS A(r);
--The query to re-build it
WITH XMLNAMESPACES('http://james.newtonking.com/projects/json' AS json)
SELECT 'true' AS [#json:Array]
,t.RowNumber AS [#RowNumber]
,'true' AS [Column/#json:Array]
,'Number' AS [Column/#Name]
,t.Number AS [Column/#Value]
,''
,'true' AS [Column/#json:Array]
,'HourFrom' AS [Column/#Name]
,t.HourFrom AS [Column/#Value]
,''
,'true' AS [Column/#json:Array]
,'HourTo' AS [Column/#Name]
,t.HourTo AS [Column/#Value]
,''
,'true' AS [Column/#json:Array]
,'Rate' AS [Column/#Name]
,t.Rate AS [Column/#Value]
,''
,'ValidationComments' AS [Column/#Name]
,'SomeValue' AS [Column/#Value]
FROM #derivedTable AS t
FOR XML PATH('row'),ROOT('root');
--Clean up (carefull with real data!)
GO
DROP TABLE #derivedTable;
DROP TABLE #xmltable
This is the result
<root xmlns:json="http://james.newtonking.com/projects/json">
<row json:Array="true" RowNumber="1">
<Column json:Array="true" Name="Number" Value="1" />
<Column json:Array="true" Name="HourFrom" Value="13.2000" />
<Column json:Array="true" Name="HourTo" Value="13.3000" />
<Column json:Array="true" Name="Rate" Value="0.8950" />
<Column Name="ValidationComments" Value="SomeValue" />
</row>
<row json:Array="true" RowNumber="2">
<Column json:Array="true" Name="Number" Value="1" />
<Column json:Array="true" Name="HourFrom" Value="13.3000" />
<Column json:Array="true" Name="HourTo" Value="13.4000" />
<Column json:Array="true" Name="Rate" Value="0.9070" />
<Column Name="ValidationComments" Value="SomeValue" />
</row>
</root>
UPDATE
Try this query, it will work for all different column lists, but it will repeat the namespace declaration. This is not wrong, but very annoying. At the moment I do not have the time to think about a hack. Let me know, if this works for you.
WITH XMLNAMESPACES('http://james.newtonking.com/projects/json' AS json)
,CTE AS
(
SELECT r.value(N'#RowNumber','int') AS RowNumber
,r.query('./*') AS TheContent
FROM #xmltable AS t
CROSS APPLY t.DataValue.nodes(N'/root/row') AS A(r)
)
SELECT CTE.TheContent AS [*]
,'ValidationComments' AS [Column/#Name]
,'SomeValue' AS [Column/#Value]
FROM CTE
FOR XML PATH('row'),ROOT('root')

How can i generate a XML without repeating an field twice

I'm trying to generated a xml using the below code. But I'm not satisfied with the output result. Below code I'm using the cursor to get the ids for the xml to be generated and update in another table. Any help is appreciated and i'm new to xml. Thanks
DECLARE #xml_var XML;
DECLARE #ID INT;
DECLARE XML_CURSOR CURSOR FOR
SELECT id
FROM xml_temp_table
WHERE id IS NOT NULL;
OPEN XML_CURSOR;
FETCH NEXT
FROM XML_CURSOR
INTO #ID;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #xml_var =
(
SELECT
(
SELECT 'Type' AS ID,
'Initial' AS VALUE,
'' AS TAG,
'true' AS VISIBLE,
Getdate() AS HISTORY,
'' AS DESCRIPTION,
'' AS COMMENT
FROM XML_TABLE d
WHERE D.XML_ID = #ID FOR XML PATH('field'),
TYPE ) AS 'field',
(
SELECT 'OwnerName' AS ID,
'Testing_XML' AS VALUE,
'' AS TAG,
'true' AS VISIBLE,
Getdate() AS HISTORY,
'' AS DESCRIPTION,
'' AS COMMENT
FROM XML_TABLE d
WHERE D.XML_ID = #ID FOR XML PATH('field'),
TYPE ) AS 'field'
FROM XML_TABLE p
WHERE P.XML_ID = #ID FOR XML PATH('Material'),
ROOT('FormValue') );
UPDATE S
SET S.XML_COL = #xml_var,
FROM LOCATION_TABLE_XML S
WHERE S.ID = #ID;
FETCH NEXT
FROM XML_CURSOR
INTO #ID;
END;
The result i'm getting is this way
<FormValue>
<Material>
<field> ----- i dont want this
<field>
<id>Type</id>
<value>Initial</value>
<tag />
<visible>true</visible>
<history>2016-11-08T16:53:16.440</history>
<description />
<comment />
</field>
<field>
<id>OwnerName</id>
<value>Testing_XML</value>
<tag />
<visible>true</visible>
<history>2016-11-08T16:53:16.440</history>
<description />
<comment />
</field>
</field> ---- i dont want this
</Material>
</FormValue>
But I want the result in this way
<FormValue>
<Material>
<field>
<id>Type</id>
<value>Initial</value>
<tag />
<visible>true</visible>
<history>2016-11-08T16:53:16.440</history>
<description />
<comment />
</field>
<field>
<id>OwnerName</id>
<value>Testing_XML</value>
<tag />
<visible>true</visible>
<history>2016-11-08T16:53:16.440</history>
<description />
<comment />
</field>
</Material>
</FormValue>
Might be enough to let the AS 'field' away. Your FOR XML PATH('field') will wrap each row with a <field> element.
The XML returning sub-selects can be seen as scalar values handled like a normal column. By providing a column alias this whole node gets a name and this name is again translated into a wrapping <field> element.
You can either erase this, or replace it with AS [node()] or with AS [*]

Resources