I have a Sql statement that returns xml of products in which root element is products. How can i add count attribute to the root element.
My sql is:
SELECT
id AS 'product_id',
name AS 'product_name'
FROM product
WHERE status = 1 AND ......
ORDER BY productid
FOR XML PATH('product'), ROOT('products')
Result is
<products>
<product>
.
.
</product>
</products>
I want to change result to
<products count="100">
<product>
.
.
</product>
</products>
SELECT
COUNT(*) AS '#count',
(
SELECT *
FROM product c1 FOR XML PATH('product'), TYPE
)
FROM product ct FOR XML PATH('products')
The easiest way to filter is to add condition in both query and sub-query:
SELECT
COUNT(*) AS '#count',
(
SELECT *
FROM product c1
WHERE c1.status = 1 AND ......
FOR XML PATH('product'), TYPE
)
FROM product ct
WHERE ct.status = 1 AND ......
FOR XML PATH('products')
Or use temp-table:
SELECT *
INTO #Temp
FROM product c1
WHERE c1.status = 1 AND ......
SELECT
COUNT(*) AS '#count',
(
SELECT *
FROM #Temp c1
FOR XML PATH('product'), TYPE
)
FROM #Temp ct
FOR XML PATH('products')
Related
Using the below SQL query I am generating an XML File like below.
SELECT CAST((SELECT
(
SELECT
ICase,
DPCRef
FROM
#HD HD WHERE HD.OrdRef=PICK.OrdRef
GROUP BY
ICase,
DPCRef
FOR XML PATH('HD'), TYPE
),
(
SELECT
PCode,
SOLnNt,
(
SELECT SSCCRef
FROM #SSCC LM
WHERE LD.OrdRef = LM.OrdRef AND LD.PCode=LM.PCode
GROUP BY
SSCCRef
FOR XML PATH(''), ROOT('SSCC'), TYPE
)
FROM #LD LD
WHERE LD.OrdRef = PICK.OrdRef
GROUP BY
PCode,
SOLnNt
FOR XML PATH('LD'), TYPE
)
FROM #PICK PICK
GROUP BY OrdRef
FOR XML PATH('PICK'), ROOT('PickOrder')) AS XML) AS OrdersPickingConfirmationXML
Result:
<PICK>
<HD>
<CustCode>PAV001</CustCode>
<OrdRef>813804</OrdRef>
</HD>
<LD>
<PCode>PROD0001</PCode>
<Rotation>14/D004193</Rotation>
<SSCC>
<SSCCRef>TEST 1</SSCCRef>
<SSCCRef>TEST1</SSCCRef>
</SSCC>
</LD>
</PICK>
Sometime there will be no data in the #SSCC table.So that time that SSCC tag is not showing in XML.If there is no data I need to show it as </SSCC>. Is there anyway that I can get blank tag </SSCC>
I have a stored procedure that looks like the following:
SELECT
ParamValues.x1.value('likeID[1]', 'VARCHAR(60)'),
COUNT(B.[userID]) AS totalLikes
FROM
#likeXML.nodes('/likes/like') AS ParamValues(x1)
JOIN
apsLikes AS B ON B.[likeID] = ParamValues.x1.value('likeID[1]', 'VARCHAR(60)')
FOR XML PATH ('likeData'), TYPE, ELEMENTS, ROOT ('root');
However this returns an error
Column 'ParamValues.x1' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
When I try and add it to the group by it says XML cannot be used in the group by clause.
What would be the correct way to format this?
Have you tried maybe using a CTE? Give the first column an alias, and remove the COUNT... like this:
; WITH XmlResults AS (
SELECT
ParamValues.x1.value('likeID[1]', 'VARCHAR(60)') AS likeId,
B.[userID] AS totalLikes
FROM
#likeXML.nodes('/likes/like') AS ParamValues(x1)
JOIN
apsLikes AS B ON B.[likeID] = ParamValues.x1.value('likeID[1]', 'VARCHAR(60)')
FOR XML PATH ('likeData'), TYPE, ELEMENTS, ROOT ('root')
)
SELECT likeId, COUNT(totalLikes)
FROM XmlResults
GROUP BY likeId
One of the ways is to do a nested select, like this:
SELECT LikeId, COUNT(Userid) FROM (
SELECT ParamValues.x1.value('likeID[1]', 'VARCHAR(60)') LikeId,
B.[userID] Userid
FROM #likeXML.nodes('/likes/like') AS ParamValues(x1)
JOIN apsLikes AS B
ON B.[likeID] = ParamValues.x1.value('likeID[1]', 'VARCHAR(60)')
FOR XML PATH ('likeData'), TYPE, ELEMENTS, ROOT ('root');
) tmp
Group By LikeId
Making a subquery and then grouping and XML'it should work :
select x1, count(Likes) from
(
SELECT
ParamValues.x1.value('likeID[1]', 'VARCHAR(60)') AS X1,
B.[userID] AS Likes
FROM
#likeXML.nodes('/likes/like') AS ParamValues(x1)
JOIN
apsLikes AS B ON B.[likeID] = ParamValues.x1.value('likeID[1]', 'VARCHAR(60)')
) t
group by X1
FOR XML PATH ('likeData'), TYPE, ELEMENTS, ROOT ('root');
I am trying to extract one record from joining the Table X and Table Y.
Below is the query am using
SELECT (select ACCOUNTSALESFORCEDETAILID
FROM sysdba.ACCOUNTSALESFORCEDETAIL AS a1
WHERE ORGSALESFORCEID = 'QWKHAA000STK'
AND ACCOUNTID = O.ACCOUNTID
and MODIFYDATE = (
SELECT MIN(MODIFYDATE) AS MODIFYDATE
FROM sysdba.ACCOUNTSALESFORCEDETAIL
WHERE ACCOUNTID = a1.ACCOUNTID
AND ORGSALESFORCEID = a1.ORGSALESFORCEID
GROUP BY ACCOUNTID, ORGSALESFORCEID
)
),
a.ORGSALESFORCEID
FROM sysdba.ACCOUNTSALESFORCEDETAIL a
JOIN sysdba.ACCOUNT O on a.ACCOUNTID=O.ACCOUNTID
WHERE O.ACCOUNTID ='A61E6C43EE71'
AND A.ORGSALESFORCEID in ('QWKHAA000STK','QWKHAA01KXNC','QWKHAA01KXNE')
Below s the result am getting now:
ACCOUNTSALESFORCEID ORGSALESFORCEID
QB46AC89800B QWKHAA000STK
QB46AC89800B QWKHAA01KXNC
QB46AC89800B QWKHAA01KXNE
But i need only First row as the output..
Please help.
Have a try like this
SELECT TOP(1) col1,ORGSALESFORCEID
FROM
(SELECT
(
SELECT
ACCOUNTSALESFORCEDETAILID
FROM
sysdba.ACCOUNTSALESFORCEDETAIL AS a1
WHERE (ORGSALESFORCEID = 'QWKHAA000STK')
AND (ACCOUNTID=O.ACCOUNTID)
AND (MODIFYDATE =( SELECT MIN(MODIFYDATE) AS MODIFYDATE
FROM sysdba.ACCOUNTSALESFORCEDETAIL
WHERE (ACCOUNTID = a1.ACCOUNTID) AND (ORGSALESFORCEID=a1.ORGSALESFORCEID)
GROUP BY ACCOUNTID, ORGSALESFORCEID)
)
) AS col1
,a.ORGSALESFORCEID
FROM
sysdba.ACCOUNTSALESFORCEDETAIL a JOIN sysdba.ACCOUNT O on a.ACCOUNTID=O.ACCOUNTID
WHERE O.ACCOUNTID='A61E6C43EE71' and A.ORGSALESFORCEID in ('QWKHAA000STK','QWKHAA01KXNC','QWKHAA01KXNE')
) AS temp
Example: I have table TableA with 3 records:
record 1:
id = 1, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee>'
record 2:
id = 2, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee><Employee id='3' name='Employee3'></Employee>'
record 3:
id = 3, value = '<Employee id='1' name='Employee1'></Employee><Employee id='2' name='Employee2'></Employee><Employee id='3' name='Employee3'></Employee><Employee id='4' name='Employee4'></Employee>'
the query:
SELECT * FROM TableA
WHERE...
How can I put the where clause to get only record 1?
Many thanks,
The problem with the data is that it doesn't contain well formed xml - you will need to wrap it before you can use the xml tools in Sql like xquery.
SELECT *
FROM
(
SELECT
Nodes.node.value('(./#id)[1]', 'int') AS EmployeeId,
Nodes.node.value('(./#name)[1]', 'varchar(50)') AS EmployeeName
FROM
(
SELECT CAST('<xml>' + value + '</xml>' AS Xml) As WrappedXml
FROM TableA
) AS x
cross apply x.WrappedXml.nodes('//Employee') as Nodes(node)
) as y
WHERE
y.EmployeeId = 1;
Inner select -wraps the xml
Middle select - standard xquery
Outer select - where filter
You haven't clarified what you mean w.r.t. get only record 1, but if you mean just the first element of each row (which coincidentally also has id = 1), you can use ROW_NUMBER() to assign a sequence:
SELECT *
FROM
(
SELECT
Nodes.node.value('(./#id)[1]', 'int') AS EmployeeId,
Nodes.node.value('(./#name)[1]', 'varchar(50)') AS EmployeeName,
ROW_NUMBER() OVER (PARTITION BY x.Id ORDER BY ( SELECT 1 )) SequenceId
FROM
(
SELECT Id, CAST('<xml>' + value + '</xml>' AS Xml) As WrappedXml
FROM TableA
) AS x
cross apply x.WrappedXml.nodes('//Employee') as Nodes(node)
) as y
WHERE SequenceId = 1;
Both Fiddles here
I tried with this query and It returns record 1 as I expect:
SELECT * FROM TableA
WHERE value.exist('(Employee[#id = 1])') = 1 and value.exist('(Employee[#id = 2])') = 1 AND value.value('count(Employee[#id])', 'int') = 2
Do you have any comments for this query? Should I use it? :)
Not able to to get the desired XML output
The following:
SELECT 1 as Tag,
0 as Parent,
sID as [Document!1!sID],
docID as [Document!1!docID],
null as [To!2!value]
FROM docSVsys with (nolock)
where docSVsys.sID = '57'
UNION ALL
SELECT 2 as Tag,
1 as Parent,
sID,
NULL,
value
FROM docMVtext
WHERE docMVtext.sID = '57'
ORDER BY [Document!1!sID],[To!2!value]
FOR XML EXPLICIT;
Produces:
<Document sID="57" docID="3.818919.C41P3UKK00BRICLAY0AR1ET2EBPYSU4SA">
<To value="Frank Ermis" />
<To value="Keith Holst" />
<To value="Mike Grigsby" />
</Document>
What I want is:
<Document sID="57">
<docID>3.818919.C41P3UKK00BRICLAY0AR1ET2EBPYSU4SA</docID>
<To>
<Value>Frank Ermis</Value>
<Value>Keith Holst</Value>
<Value>Mike Grigsby</Value>
</To>
</Document>
Can I get that ouput with FOR XML?
Ok I get they may be technically equivalent.
What I want and what I need are not the same.
Using xDocument for this is is SLOW.
There are millions of documents and need to XML up to 1 million at a time to XML.
The TSQL FOR XML is super fast.
I just need to get FOR XML to format.
The solution (based on accepted answer):
SELECT top 4
[sv].[sID] AS '#sID'
,[sv].[sParID] AS '#sParID'
,[sv].[docID] AS 'docID'
,[sv].addDate as 'addDate'
,(SELECT [value] AS 'value'
FROM [docMVtext] as [mv]
WHERE [mv].[sID] = [sv].[sID]
AND [mv].[fieldID] = '113'
ORDER BY [mv].[value]
FOR XML PATH (''), type
) AS "To"
,(SELECT [value] AS 'value'
FROM [docMVtext] as [mv]
WHERE [mv].[sID] = [sv].[sID]
AND [mv].[fieldID] = '130'
ORDER BY [mv].[value]
FOR XML PATH (''), type
) AS "MVtest"
FROM [docSVsys] as [sv]
WHERE [sv].[sID] >= '57'
ORDER BY
[sv].[sParID], [sv].[sID]
FOR XML PATH('Document'), root('Documents')
Produces:
<Documents>
<Document sID="57" sParID="57">
<docID>3.818919.C41P3UKK00BRICLAY0AR1ET2EBPYSU4SA</docID>
<addDate>2011-10-28T12:26:00</addDate>
<To>
<value>Frank Ermis</value>
<value>Keith Holst</value>
<value>Mike Grigsby</value>
</To>
<MVtest>
<value>MV test 01</value>
<value>MV test 02</value>
<value>MV test 03</value>
<value>MV test 04</value>
</MVtest>
</Document>
<Document sID="58" sParID="57">
<docID>3.818919.C41P3UKK00BRICLAY0AR1ET2EBPYSU4SA.1</docID>
<addDate>2011-10-28T12:26:00</addDate>
</Document>
<Document sID="59" sParID="59">
<docID>3.818920.KJKP5LYKTNIODOEI4JDOKJ2BXJI5P0BIA</docID>
<addDate>2011-10-28T12:26:00</addDate>
<To>
<value>Vladimir Gorny</value>
</To>
</Document>
<Document sID="60" sParID="59">
<docID>3.818920.KJKP5LYKTNIODOEI4JDOKJ2BXJI5P0BIA.1</docID>
<addDate>2011-10-28T12:26:00</addDate>
</Document>
</Documents>
Now what I need to do is to add a DispName attribute to the element MVtext. Attribute cannot have any spaces and I would like to include the friendly name e.g. Multi Value Text.
Try something like this (untested, since I don't have your database tables to test against...):
SELECT
sv.sID AS '#sID',
sv.docID AS 'docID',
(SELECT
value AS 'value'
FROM
dbo.docMVtext mv
WHERE
mv.sID = sv.sID
ORDER BY mv.value
FOR XML PATH (''), TYPE) AS 'To'
FROM
dbo.docSVsys sv
WHERE
sv.sID = '57'
ORDER BY
sv.sID
FOR XML PATH('Document')
Does that give you what you're looking for?? And don't you agree with John and me: this is much simpler than FOR XML EXPLICIT.....
From Examples: Using PATH Mode:
USE AdventureWorks2008R2;
GO
SELECT ProductModelID AS "#ProductModelID",
Name AS "#ProductModelName",
(SELECT ProductID AS "data()"
FROM Production.Product
WHERE Production.Product.ProductModelID =
Production.ProductModel.ProductModelID
FOR XML PATH ('')
) AS "#ProductIDs",
(
SELECT Name AS "ProductName"
FROM Production.Product
WHERE Production.Product.ProductModelID =
Production.ProductModel.ProductModelID
FOR XML PATH (''), type
) AS "ProductNames"
FROM Production.ProductModel
WHERE ProductModelID= 7 OR ProductModelID=9
FOR XML PATH('ProductModelData');