Reputation: 3
in my database I have an XML-column that looks like this:
<Root>
<Row>
<Einheit>Stck</Einheit>
<Faktor>1</Faktor>
<VkPreisEinheit>1</VkPreisEinheit>
<VkMengenEinheit>1</VkMengenEinheit>
<EkMengenEinheit>1</EkMengenEinheit>
<StcklEinheit>1</StcklEinheit>
<StcklDefinition>1</StcklDefinition>
<KonsumentenEinheit>1</KonsumentenEinheit>
</Row>
<Row>
<Einheit>Stück</Einheit>
<Faktor>100</Faktor>
<EinheitFaktor>Stck</EinheitFaktor>
<EkPreisEinheit>1</EkPreisEinheit>
</Row>
</Root>
What I want to achieve ist that I get the value from 'Faktor' only from the row where 'EkPreisEinheit' is 1
I have tried something with:
CASE WHEN isnull(convert(xml,xmlcolumn).value('(/Root/Row/EkPreisEinheit)[1]','nvarchar(max)'),'') = '1'
THEN isnull(convert(xml,xmlcolumn).value('(/Root/Row/Faktor)[1]','nvarchar(max)'),'')
WHEN isnull(convert(xml,xmlcolumn).value('(/Root/Row/EkPreisEinheit)[2]','nvarchar(max)'),'') = '1'
THEN isnull(convert(xml,xmlcolumn).value('(/Root/Row/Faktor)[2]','nvarchar(max)'),'')
ELSE ''
END AS Faktor
which would work if EKPreiseinheit would be found in both columns, but it only is in one. Also it could be that it is in the first row, or in the third if there was any. Is there any way to tackle this?
Upvotes: 0
Views: 73
Reputation: 22157
Here is a solution for you. Few points to mention. (0) It handles XML directly from the table. (1) It converts XML data into XML data type via TRY_CAST()
. So it will emit NULL when XML is not well-formed without generating any error. (2) It checks via XPath
predicate for the <Row>
element where 'EkPreisEinheit' element value is 1, and filters out anything else.
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, xmlcolumn NVARCHAR(MAX));
INSERT INTO @tbl
VALUES (N'<Root>
<Row>
<Einheit>Stck</Einheit>
<Faktor>1</Faktor>
<VkPreisEinheit>1</VkPreisEinheit>
<VkMengenEinheit>1</VkMengenEinheit>
<EkMengenEinheit>1</EkMengenEinheit>
<StcklEinheit>1</StcklEinheit>
<StcklDefinition>1</StcklDefinition>
<KonsumentenEinheit>1</KonsumentenEinheit>
</Row>
<Row>
<Einheit>Stück</Einheit>
<Faktor>100</Faktor>
<EinheitFaktor>Stck</EinheitFaktor>
<EkPreisEinheit>1</EkPreisEinheit>
</Row>
</Root>');
-- DDL and sample data population, end
;WITH rs AS
(
SELECT ID, TRY_CAST(xmlcolumn AS XML) AS xml_data
from @tbl
)
SELECT ID
, col.value('(Faktor/text())[1]','INT') AS Faktor
, col.value('(EkPreisEinheit/text())[1]','INT') AS EkPreisEinheit
FROM rs as tbl
CROSS APPLY tbl.xml_data.nodes('/Root/Row[EkPreisEinheit="1"]') AS tab(col);
Output
+----+--------+----------------+
| ID | Faktor | EkPreisEinheit |
+----+--------+----------------+
| 1 | 100 | 1 |
+----+--------+----------------+
Upvotes: 0
Reputation: 95554
When dealing with multiple of the same node, you want to use nodes
in the FROM
to create a row for each one. This means you end up with something like this:
DECLARE @XML xml = '<Root>
<Row>
<Einheit>Stck</Einheit>
<Faktor>1</Faktor>
<VkPreisEinheit>1</VkPreisEinheit>
<VkMengenEinheit>1</VkMengenEinheit>
<EkMengenEinheit>1</EkMengenEinheit>
<StcklEinheit>1</StcklEinheit>
<StcklDefinition>1</StcklDefinition>
<KonsumentenEinheit>1</KonsumentenEinheit>
</Row>
<Row>
<Einheit>Stück</Einheit>
<Faktor>100</Faktor>
<EinheitFaktor>Stck</EinheitFaktor>
<EkPreisEinheit>1</EkPreisEinheit>
</Row>
</Root>';
WITH YourTable AS(
SELECT V.YourXML
FROM (VALUES(@XML)) V(YourXML))
SELECT R.R.value('(EkPreisEinheit/text())[1]','int') AS EkPreisEinheit
FROM YourTable YT
--Due to the misuse of datatypes you'll need a CONVERT in a VALUES clause here instead
CROSS APPLY YT.YourXML.nodes('/Root/Row') R(R)
WHERE R.R.value('(EkPreisEinheit/text())[1]','int') IS NOT NULL;
Upvotes: 1