2

I have XML data stored in SQL Server database in one column.

<data>
    <row>
<element name="product">Piston</element>
<element name="number">1.2</element>
    </row>
<row>
<element name="product">Piston Ring</element>
<element name="number">2</element>
    </row>
<row>
<element name="product">Piston</element>
<element name="number">1.5</element>
    </row>
</data>

Is there a way I can get result in following format?

------------------------------
Product    | Count
------------------------------
Piston     |   2
Piston Ring|   1
------------------------------

I tried with Xpath which gives me count of anything, but not sure if I can group by product and then get a count.

I'm looking for something like (In SQL Query)

SELECT Product, Count(Product) FROM ABC
GROUP BY Product
Shnugo
  • 66,100
  • 9
  • 53
  • 114
A3006
  • 1,051
  • 1
  • 11
  • 28

3 Answers3

5

You cannot use the XML methods directly within GROUP BYso I'd use a CTE. First you get table data out of your XML, than you can do a "normal" COUNT with GROUP BY:

DECLARE @xml XML=
'<data>
  <row>
    <element name="product">Piston</element>
    <element name="number">1.2</element>
  </row>
  <row>
    <element name="product">Piston Ring</element>
    <element name="number">2</element>
  </row>
  <row>
    <element name="product">Piston</element>
    <element name="number">1.5</element>
  </row>
</data>';

;WITH MyRows AS
(
    SELECT OneRow.value('(element[@name="product"])[1]','varchar(max)') AS Product
    FROM @xml.nodes('/data/row') AS A(OneRow)
)
SELECT Product,COUNT(Product) AS [Count]
FROM MyRows
GROUP BY Product
Shnugo
  • 66,100
  • 9
  • 53
  • 114
  • Hi, this is giving the expected result, but taking too much time. ( I checked with very large data). But thanks, it works. – A3006 Feb 26 '16 at 09:46
  • @Anand,you might think about an XML index. I answered a similar question shortly: http://stackoverflow.com/q/35575990/5089204 – Shnugo Feb 26 '16 at 09:48
  • @Shnugo. Can you please explain how the following works? SELECT OneRow.value('(element[@name="product"])[1]','varchar(max)') AS Product FROM @xml.nodes('/data/row') AS A(OneRow) – Mark Jan 28 '17 at 12:40
1
DECLARE @x XML=
'<data>
  <row>
    <element name="product">Piston</element>
    <element name="number">1.2</element>
  </row>
  <row>
    <element name="product">Piston Ring</element>
    <element name="number">2</element>
  </row>
  <row>
    <element name="product">Piston</element>
    <element name="number">1.5</element>
  </row>
</data>'

SELECT val, COUNT_BIG(1)
FROM (
    SELECT val = t.c.value('.', 'VARCHAR(100)')
    FROM @x.nodes('/data/row/element[@name="product"]') t(c)
) t
GROUP BY val
OPTION (OPTIMIZE FOR (@x = NULL))

Output -

------------------- --------------------
Piston              2
Piston Ring         1
Devart
  • 119,203
  • 23
  • 166
  • 186
0

I would use CROSS APPLY from table to XML column

CREATE TABLE XMLwithOpenXML (
     id INT IDENTITY PRIMARY KEY
   , XMLData XML
   , LoadedDateTime DATETIME
)
GO

DECLARE @xml XML = '
<data> 
    <row>
        <element name="product">Piston</element>
        <element name="number">1.2</element>
    </row>
    <row>
        <element name="product">Piston Ring</element>
        <element name="number">2</element>
    </row>
    <row>
        <element name="product">Piston</element>
        <element name="number">1.5</element>
    </row>
</data>'

INSERT INTO XMLwithOpenXML (XMLData, LoadedDateTime)
SELECT @xml, GETDATE()

SELECT Product, COUNT(Product) AS ProdCount
FROM (
    SELECT
         n.C.value('(element[@name="product"])[1]', 'varchar(100)') product
       , n.C.value('(element[@name="number"])[1]', 'varchar(100)') number
    FROM XMLwithOpenXML
    CROSS APPLY XMLData.nodes('/data/row') n(C)
) A
GROUP BY Product
Devart
  • 119,203
  • 23
  • 166
  • 186
bmsqldev
  • 2,627
  • 10
  • 31
  • 65