How do I query and XML column in SQL by attribute? - sql

I need to query an XML column and return rows that match certain criteria. The XML column is called Configuration and a sample value looks like:
<feed>
<schedule>
<arrivalSla dayOfWeek="monday" time="02:00:00" />
</schedule>
</feed>
I want to return all rows that have a time of "04:00:00".
I have found out how to query a simple XML column that does not have attributes such as time. I need to query on the attribute however.

You can try something like this:
DECLARE #Sample TABLE (ID INT NOT NULL, Configuration XML)
INSERT INTO #Sample (ID, Configuration)
VALUES (1, '<feed>
<schedule>
<arrivalSla dayOfWeek="Monday" time="02:00:00" />
</schedule>
</feed>'), (2, '<feed>
<schedule>
<arrivalSla dayOfWeek="Tuesday" time="04:00:00" />
</schedule>
</feed>'), (3, '<feed>
<schedule>
<arrivalSla dayOfWeek="Wednesday" time="05:00:00" />
</schedule>
</feed>')
SELECT
ID,
XmlDayOfWeek = s.Configuration.value('(/feed/schedule/arrivalSla/#dayOfWeek)[1]', 'varchar(50)')
FROM
#Sample s
WHERE
s.Configuration.value('(/feed/schedule/arrivalSla/#time)[1]', 'varchar(20)') = '04:00:00'
This will produce a result of:
ID XmlDayOfWeek
------------------
2 Tuesday

Related

How to search XML in SQL Server

I looked at some threads but I think I'm missing something in Microsoft SQL Server (SSMS).
I have XML in column defined as XML datatype that looks like this:
(I erased stuff before this not sure if it's needed)
<ItemGroupData ItemGroupOID="TEST" TransactionType="Insert">
<ItemData ItemOID="TEACHER" Value="145"/>
<ItemData ItemOID="AGE" Value="50" />
</ItemGroupData>
<ItemGroupData ItemGroupOID="TEST" TransactionType="Insert">
<ItemData ItemOID="TEACHER" Value="151"/>
<ItemData ItemOID="AGE" Value="42" />
</ItemGroupData>
There's stuff I truncated but what is the most optimal way to locate the XML file where teacher 145 is and they can be in any of the Itemdata groups?
I can find it like:
SELECT
CAST(XML AS nvarchar(max)) AS test
FROM
table1
WHERE
XML LIKE '%14%'
but I am looking into learning different ways without casting unless that is the most optimal way?
Yes, that "stuff before" that you erased will be very important! You need to build up XPath expressions to select individual pieces from the XML - and those depend on everything from the root on down! Also - you might have XML namespaces that are defined in the "stuff before" - which you need to respect to get any results.
Anyhoo - ASSUMING you have just a <root>....</root> node before your XML, you could get your desired result like this :
DECLARE #XmlTbl TABLE (ID INT NOT NULL, XmlData XML)
INSERT INTO #XmlTbl (ID, XmlData)
VALUES (1,
'<root><ItemGroupData ItemGroupOID="TEST" TransactionType="Insert">
<ItemData ItemOID="TEACHER" Value="145"/>
<ItemData ItemOID="AGE" Value="50" />
</ItemGroupData>
<ItemGroupData ItemGroupOID="TEST" TransactionType="Insert">
<ItemData ItemOID="TEACHER" Value="151"/>
<ItemData ItemOID="AGE" Value="42" />
</ItemGroupData></root>')
SELECT
t.ID,
XC.value('(#ItemGroupOID)', 'varchaR(50)') AS ItemGroupOID,
XC.value('(#TransactionType)', 'varchaR(50)') AS TransactionType,
XC2.value('#ItemOID', 'varchar(25)') AS ItemOID,
XC2.value('#Value', 'int') AS value
FROM
#XmlTbl t
CROSS APPLY -- "enumerate" the <ItemGroupData> nodes under <root>
XmlData.nodes('/root/ItemGroupData') AS XT(XC)
CROSS APPLY -- "enumerate" the <ItemData> subnodes
XC.nodes('ItemData') AS XT2(XC2)
WHERE
XC.value('(ItemData/#Value)[1]', 'int') = 145
This would return these results:
ID
ItemGroupOID
TransactionType
ItemOID
value
1
TEST
Insert
TEACHER
145
1
TEST
Insert
AGE
50
SQL Server supports powerful XQuery language to deal with the XML data type.
Please try the following solution.
It is using XPath predicate [#Value=sql:variable("#TeacherValue")] to search directly in the XML data type.
The sql:variable("#TeacherValue") construct allows to pass a parameter to it.
Also, SQL Server supports XML indexes for that.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, XmlData XML);
INSERT INTO #tbl (XmlData) VALUES
(N'<root>
<ItemGroupData ItemGroupOID="TEST" TransactionType="Insert">
<ItemData ItemOID="TEACHER" Value="145"/>
<ItemData ItemOID="AGE" Value="50"/>
</ItemGroupData>
<ItemGroupData ItemGroupOID="TEST" TransactionType="Insert">
<ItemData ItemOID="TEACHER" Value="151"/>
<ItemData ItemOID="AGE" Value="42"/>
</ItemGroupData>
</root>');
-- DDL and sample data population, end
DECLARE #TeacherValue INT = 145;
SELECT t.ID
, p.value('#ItemGroupOID', 'VARCHAR(50)') AS ItemGroupOID
, p.value('#TransactionType', 'VARCHAR(50)') AS TransactionType
, c.value('#ItemOID', 'VARCHAR(25)') AS ItemOID
, c.value('#Value', 'INT') AS value
FROM #tbl AS t
CROSS APPLY XmlData.nodes('/root/ItemGroupData[ItemData[#ItemOID="TEACHER"
and #Value=sql:variable("#TeacherValue")]]') AS t1(p)
CROSS APPLY t1.p.nodes('ItemData') AS t2(c);
Output
ID
ItemGroupOID
TransactionType
ItemOID
value
1
TEST
Insert
TEACHER
145
1
TEST
Insert
AGE
50

SQL string value

Have a large XML file stored in a field within a table, many values have already been extracted and stored in the table, but I'm looking to capture (2) additional: account type = "current" status="X" and account type = "former" status ="Y". In the partial output below there is no former account type so I need a strategy for missing as well.
<ncf_report xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://cp.com/rules/client">
<admin>
<product_reference>12345678901234</product_reference>
<report_type>XXXXXXX</report_type>
<status>XXXXXXXX</status>
<ownership>XXXXXXX</ownership>
<report_code>1234</report_code>
<report_description>XXXXXXXXXXXXXXXXX</report_description>
<purpose>XXXXXXXX</purpose>
<date_request_ordered>mm/dd/yyyy</date_request_ordered>
<date_request_received>mm/dd/yyyy</date_request_received>
<date_request_completed>mm/dd/yyyy</date_request_completed>
<time_report_processed>01234</time_report_processed>
<multiple_scores_ordered>false</multiple_scores_ordered>
<vendor name="XXXXXXXXXXXXX" address="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<report>
<sequence>0000000000</sequence>
<count>0000000000</count>
</report>
</admin>
<report>
<alerts_scoring>
<scoring>
<score status="XXXXXXXXXX">
<model_label>XXXXXXXXXXXXXXXXX</model_label>
<score>123</score>
<rating_state>XX</rating_state>
<classification>XXXXXXXXXXXXXXXXX</classification>
<reason_codes>
<code>05</code>
<description>XXXXXXXXXXXXXXXXX</description>
</reason_codes>
<reason_codes>
<code>04</code>
<description>XXXXXXXXXXXXXXXXX</description>
</reason_codes>
<reason_codes>
<code>10</code>
<description>XXXXXXXXXXXXXXXXX</description>
</reason_codes>
<reason_codes>
<code>27</code>
<description>XXXXXXXXXXXXXXXXX</description>
</reason_codes>
</score>
</scoring>
<general>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</general>
<general>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</general>
<general>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</general>
</alerts_scoring>
<vendor_dataset>
<subjects>
<subject type="Primary" relationship_to_data="Subject">
<name type="Report Subject">
<first>XXXX</first>
<middle>X</middle>
<last>XXXX</last>
</name>
<name type="Alias">
<first>XXXXXXXXXX</first>
<last>XXXXXXXX</last>
</name>
<birth_date>mm/dd/yyyy</birth_date>
<ssn>999999999</ssn>
<address type="residence" ref="1" />
<address type="former" ref="2" />
<address type="former" ref="3" />
</subject>
</subjects>
<addresses>
<address id="1">
<house>1234</house>
<street1>sample</street1>
<city>sample</city>
<state>XX</state>
<postalcode>12345</postalcode>
<zip4>1234</zip4>
<date_first_at_address>mm/dd/yyyy</date_first_at_address>
<date_last_at_address>mm/dd/yyyy</date_last_at_address>
</address>
<address id="X">
<house>1234</house>
<street1>XXXXXXXXX</street1>
<city>XXXXXXXXX</city>
<state>XX</state>
<postalcode>12345</postalcode>
<zip4>1234</zip4>
<date_first_at_address>mm/dd/yyyy</date_first_at_address>
<date_last_at_address>mm/dd/yyyy</date_last_at_address>
</address>
</addresses>
</vendor_dataset>
<summary>
<date_oldest_trade>mm/dd/yyyy</date_oldest_trade>
<date_latest_trade>mm/dd/yyyy</date_latest_trade>
<date_latest_activity>mm/dd/yyyy</date_latest_activity>
<includes_bankruptcies flag="false" />
<includes_other_records public_records="false" collection="false" consumer_statement="false" />
<credit_range high="12345" low="123" number_trade_lines="123" />
<account_status_counters>
<!-- here --> <account type="current" description="Pays Account as Agreed" status="1">12</account>
</account_status_counters>
<account_summaries>
<account type="Open-ended">
<number_accounts>0</number_accounts>
<total_owed>0</total_owed>
<total_past_due>0</total_past_due>
<high_amount>0</high_amount>
</account>
<account type="Revolving">
<number_accounts>00</number_accounts>
<total_owed>1234</total_owed>
<total_past_due>0</total_past_due>
<high_amount>12345</high_amount>
</account>
<account type="Installment">
<number_accounts>00</number_accounts>
<total_owed>12345</total_owed>
<total_past_due>0</total_past_due>
<high_amount>123456</high_amount>
</account>
</account_summaries>
<inquiry_history count="0" />
</summary>
<employment_history>
<employment_primary_subject>
<job entry="current" indirectly_verified="false">
<employer>
<name>XXXXXXXXXXX</name>
</employer>
</job>
<job entry="first_former" indirectly_verified="false">
<employer>
<name>XXXXXXXX</name>
<city>XXXXXXX</city>
<state>XX</state>
</employer>
</job>
</employment_primary_subject>
</employment_history>
<trade_account_activity>
<credit_trades>
<credit_trade automated_tape_supplier="false">
<reporting_member>
<number>1234X1234</number>
<name>XXX/1234</name>
</reporting_member>
<account>
<type>XXXXXXXXX</type>
<terms>XXX</terms>
<months_reviewed>00</months_reviewed>
<designator>XXXXXXXX(XXXXX)</designator>
</account>
<date_reported>mm/dd/yyyy</date_reported>
<date_opened>mm/dd/yyyy</date_opened>
<date_last_activity>mm/dd/yyyy</date_last_activity>
<current_rate>XXXXXXXXXXXXXXXXX</current_rate>
<highest_amount>1234</highest_amount>
<balance_amount>00</balance_amount>
<past_due_amount>00</past_due_amount>
<messages>
<message code="XX">XXXXXXXXXXXXXX</message>
<message code="XX">XXXXXXXXXXXXXX</message>
</messages>
</credit_trade>
</account>
<date_reported>mm/dd/yyyy</date_reported>
<date_opened>mm/dd/yyyy</date_opened>
<date_last_activity>mm/dd/yyyy</date_last_activity>
<current_rate>XXXXXXXXXXXXXXXXXXXXXX</current_rate>
<highest_amount>123456</highest_amount>
<balance_amount>123456</balance_amount>
<past_due_amount>0</past_due_amount>
<messages>
<message code="XX">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</message>
</messages>
</credit_trade>
</credit_trades>
</trade_account_activity>
<inquiry_history>
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345X1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345Y1234" />
<inquiry date="mm/dd/yyyy" name="CXXXXXXXXXXXXXXXXXXXXXXX" member="12345Z1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX & X" member="12345W1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345V1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345U1234" />
<inquiry date="mm/dd/yyyy" name="XXXXXXXXXXXXXXXXXXXXXXX" member="12345T1234" />
</inquiry_history>
</report>
</ncf_report>
I'm looking to extract the X value from account type = "current" status="X" and and Y value if an account type = "former" exists. In this case the value 1. added to XML to highlight area of interest. I started by pairing down the data set into a temp table.
select id,
LEFT(SUBSTRING(CreditscoreXML,charindex('<account type="current"',CreditscoreXML),charindex('</account>',CreditscoreXML)),charindex('">',SUBSTRING(CreditscoreXML,charindex('<account type="current"',CreditscoreXML),charindex('</account>',CreditscoreXML)))) [Current_Status]
select
Current_Status, --just so I see output is correct in temp table
substring(Current_Status, charindex('status="',
Current_Status)+8,len(Current_Status)-charindex('status',Current_Status)) [Current_Status]
from #TempCurrent
From here I further tried to refine the text search. Trying to figure out how to eliminate the " after 1 or a better solution to extract both current and former status, former can be missing, need this grouped by Id.
Current Output
Current_Worse_Score Current_Worse_Score Former_Worse
Original Text 1"
Rather than manipulating string data try using the built-in XML functions in SQL Server to make your life easier. For example:
create table dbo.Foo (
id int not null,
bar xml not null
);
insert dbo.Foo (id, bar) values (47, N'<ncf_report xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://cp.com/rules/client">
<!-- the rest of your xml... -->
</ncf_report>')
;with xmlnamespaces(default 'http://cp.com/rules/client')
select
id,
x.a.value(N'#description', N'nvarchar(50)') as [Description],
x.a.value(N'#status', N'nvarchar(50)') as [Status],
x.a.value(N'.', N'nvarchar(50)') as [Account]
from dbo.Foo
cross apply bar.nodes(N'/ncf_report/report/summary/account_status_counters/account[#type="current"]') x(a)
Which yields the result...
id Description Status Account
47 Pays Account as Agreed 1 12
You can use the built-in XML data type methods to query required values from an XML instance which is stored as XMLType column.
DECLARE #X XML;
SET #X = '<ncf_report xmlns:xsd="http://www.w3.org/2001/XMLSchema" ....>'; -- provided XML instance
CREATE TABLE NCFREPORT
(
ncfreportcol XML NOT NULL
);
INSERT INTO ncfreport (ncfreportcol) values (#X); -- inserting the XML instance stored temporarily in above variable X
WITH xmlnamespaces ('http://cp.com/rules/client' as NR)
SELECT T.acc.value('(#status)[1]', 'int') AS Status,
T.acc.value('(#type)[1]', 'varchar(20)') AS AccType,
T.acc.value('(text())[1]', 'int') AS Acc
FROM ncfreport cross apply ncfreport.ncfreportcol.nodes ('/NR:ncf_report/NR:report/NR:summary/NR:account_status_counters/NR:account') as t(acc);
This will result in the following output:
Status AccType Acc
1 current 12
It will produce one row in the output for each account if you have multiple account tags defined in the XML instance. I also noticed that there are missing opening or closing tags in the above XML fragment. It would be a good idea to also have a look at validating the XML before entering into the table. Please have a look at various XML data type methods here - https://learn.microsoft.com/en-us/sql/t-sql/xml/xml-data-type-methods?view=sql-server-ver15

How to get value from a node in XML via SQL Server

I've found several pieces of information online about this but I can't get it working for the life of me.
This is the XML I have:
I need to extract the ID & Name value for each node. There are a lot.
I tried to do this but it returns NULL:
select [xml].value('(/Alter/Object/ObjectDefinition/MeasureGroup/Partitions/Partition/ID)[1]', 'varchar(max)')
from test_xml
I understand the above would return only 1 record. My question is, how do I return all records?
Here's the XML text (stripped down version):
<Alter xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" AllowCreate="true" ObjectExpansion="ExpandFull">
<ObjectDefinition>
<MeasureGroup xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>ts_homevideo_sum_20140430_76091ba1-3a51-45bf-a767-f9f3de7eeabe</ID>
<Name>table_1</Name>
<StorageMode valuens="ddl200_200">InMemory</StorageMode>
<ProcessingMode>Regular</ProcessingMode>
<Partitions>
<Partition>
<ID>123</ID>
<Name>2012</Name>
</Partition>
<Partition>
<ID>456</ID>
<Name>2013</Name>
</Partition>
</Partitions>
</MeasureGroup>
</ObjectDefinition>
</Alter>
You need something like this:
DECLARE #MyTable TABLE (ID INT NOT NULL, XmlData XML)
INSERT INTO #MyTable (ID, XmlData)
VALUES (1, '<Alter xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" AllowCreate="true" ObjectExpansion="ExpandFull">
<ObjectDefinition>
<MeasureGroup xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>ts_homevideo_sum_20140430_76091ba1-3a51-45bf-a767-f9f3de7eeabe</ID>
<Name>table_1</Name>
<StorageMode valuens="ddl200_200">InMemory</StorageMode>
<ProcessingMode>Regular</ProcessingMode>
<Partitions>
<Partition>
<ID>123</ID>
<Name>2012</Name>
</Partition>
<Partition>
<ID>456</ID>
<Name>2013</Name>
</Partition>
</Partitions>
</MeasureGroup>
</ObjectDefinition>
</Alter>')
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/analysisservices/2003/engine')
SELECT
tbl.ID,
MeasureGroupID = xc.value('(ID)[1]', 'varchar(200)'),
MeasureGroupName = xc.value('(Name)[1]', 'varchar(200)'),
PartitionID = xp.value('(ID)[1]', 'varchar(200)'),
PartitionName = xp.value('(Name)[1]', 'varchar(200)')
FROM
#MyTable tbl
CROSS APPLY
tbl.XmlData.nodes('/Alter/ObjectDefinition/MeasureGroup') AS XT(XC)
CROSS APPLY
XC.nodes('Partitions/Partition') AS XT2(XP)
WHERE
ID = 1
First of all, you must respect and include the default XML namespace defined in the root of your XML document.
Next, you need to do a nested call to .nodes() to get all <MeasureGroup> and all contained <Partition> nodes, so that you can reach into those XML fragments and extract the ID and Name from them.
This should then result in something like this as output:

how to use GETDATE() function with xml exist() function

I have a scenario where i have to use xml exist() function in where clause of the query. where i have to compare date from the xml with the current date. When i try to use GETDATE() function, i get below error.
The argument 1 of the XML data type method "exist" must be a string literal.
Please consider below query for your reference.
SELECT
TRY_CONVERT(datetime,NULLIF(t.x.value('(./Expire)[1]','varchar(max)'), '')) as expiration_date
FROM VW_Analytics_Base_Facts(nolock) BaseFact
CROSS APPLY BaseFact.Fact.nodes ('/Fact/Grant') t(x)
WHERE TYPE = '/asset/portfolio/option'
AND BaseFact.Fact.exist('./Expire[(text()[1] cast as xs:date?) le xs:date("' + CONVERT(NVARCHAR(max),CONVERT(date,GETDATE()))+'")]')=1
Thanks in advance.
Below is the updated working query.
DECLARE #tbl TABLE (XmlCol xml)
INSERT INTO #tbl VALUES
('<option>
<OptionName>Option 1</OptionName>
<grant>
<GrantName>Grant 1</GrantName>
<schedules>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>1/1/2018</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>2/1/2018</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>3/1/2018</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
</schedules>
</grant>
<grant>
<GrantName>Grant 2</GrantName>
<schedules>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>1/1/2019</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>2/1/2019</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
<schedule>
<scheduleID></scheduleID>
<scheduleName></scheduleName>
<scheduleDate>3/1/2019</scheduleDate>
<scheduleAmount></scheduleAmount>
</schedule>
</schedules>
</grant>
</option>'
)
SELECT e.XmlCol.value('(/option/OptionName)[1]', 'varchar(100)'),
t.x.value('../.././GrantName[1]','varchar(100)') GrantName,
t.x.value('(./scheduleDate)[1]', 'varchar(100)') scheduleDate
FROM #tbl e
CROSS APPLY (SELECT CONVERT(date,GETDATE())) dt(today)
cross apply e.XmlCol.nodes ('/option/grant/schedules/schedule') t(x)
WHERE e.XmlCol.exist('./scheduleDate[(text()[1] cast as xs:date?) le sql:column("dt.today")]')=1
i want to filter the record based on schedule date.
This query is a part of view so i cannot declare a variable.
Next time please append a stand-alone working sample with DDL, data inserts, your own code and the expected output. This would allow to test a solution.
In this case I have to guess and post untested:
SELECT
TRY_CONVERT(datetime,NULLIF(t.x.value('(./Expire)[1]','varchar(max)'), '')) as expiration_date
FROM VW_Analytics_Base_Facts(nolock) BaseFact
CROSS APPLY (SELECT CONVERT(date,GETDATE())) dt(today)
CROSS APPLY BaseFact.Fact.nodes ('/Fact/Grant') t(x)
WHERE TYPE = '/asset/portfolio/option'
AND BaseFact.Fact.exist('./Expire[(text()[1] cast as xs:date?) le sql:column("dt.today")]')=1;
This query will use one more CROSS APPLY in order to include the information in the resultset. The function sql:column() allows to use a result set's column within XQuery without breaking the must-be-a-literal rule (use sql:variable() to get hands on a declared variable's value).
UPDATE
Your idea to apply the filter as early as possible is not wrong. This was even better as predicate within .nodes(). But you have to rely on an implicit cast, something you should not do.
The expression (text()[1] cast as xs:date?) will use the system's culture. A value like 1/3/2018 might be taken as 1st of March or as 3rd of January. Therefore I'd suggest to read the value as string and use CONVERT with the appropriate style:
Btw: I use two times CROSS APPLY with .nodes() to avoid backward navigation (../../)
WITH DerivedTable AS
(
SELECT e.XmlCol.value('(/option/OptionName/text())[1]', 'varchar(100)') AS OptionName
,gr.value('(GrantName/text())[1]','varchar(100)') GrantName
,sch.value('(scheduleID/text())[1]', 'varchar(100)') scheduleID --use appropriate type, might be "int"
,sch.value('(scheduleName/text())[1]', 'varchar(100)') scheduleName
,CONVERT(DATE,sch.value('(scheduleDate/text())[1]', 'varchar(100)'),110) scheduleDate --fetch this as varchar, to avoid implicit casts
--use CONVERT with the appropriate style to get a real date
--110 is mdy, 103 is dmy
,sch.value('(scheduleAmount/text())[1]', 'varchar(100)') scheduleAmount --use appropriate type, might be "decimal(12,4)"
FROM #tbl e
CROSS APPLY e.XmlCol.nodes ('/option/grant') A(gr)
CROSS APPLY A.gr.nodes('schedules/schedule') B(sch)
)
SELECT dt.*
FROM DerivedTable dt
--use a simple WHERE here
This will return the XML's content as derived table. Use a simple WHERE to filter this derived table.
Below is the work around which worked for me.
SELECT e.XmlCol.value('(/option/OptionName)[1]', 'varchar(100)') OptionName,
t.x.value('../.././GrantName[1]','varchar(100)') GrantName,
t.x.value('(./scheduleDate)[1]', 'varchar(100)') scheduleDate
FROM #tbl e
CROSS APPLY e.XmlCol.nodes ('/option/grant/schedules/schedule') t(x)
WHERE TRY_CONVERT(datetime,NULLIF(t.x.value('(./scheduleDate)[1]','varchar(max)'), '')) BETWEEN CONVERT(date,GETDATE()) AND DATEADD(YEAR, 1, GETDATE())

Parse an xml data column on a table in SQL Server 2012

How do I parse an xml column on a table of data in SQL Server 2012
Sample data
<GetOfferAvailabilityResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p3="http://somewebsite.com/v2.0" xmlns="http://somewebsite.com/v2.0" p3:TransactionID="281234567">
<p3:RuleResultList xsi:nil="true" />
<p3:ResultList>
<p3:ProviderResult p3:ProviderID="01" p3:ResultID="1234" p3:ResultType="NotAvailable" p3:ResultCode="NotAvailable" p3:BrokerID="55" p3:Structure="None">
<p3:EntityState>None</p3:EntityState>
<p3:ResultText>No Orders returned</p3:ResultText>
<p3:ShortDescription>Not Available</p3:ShortDescription>
<p3:LongDescription>We're sorry, but offers are currently not available for your service address.</p3:LongDescription>
<p3:ResultAction>ErrorMessage</p3:ResultAction>
<p3:SourceResultCode xsi:nil="true" />
</p3:ProviderResult>
</p3:ResultList>
</GetOfferAvailabilityResponse>'
I tried:
DECLARE #x xml
SET #x =
DECLARE #test TABLE (ID INT, XmlRule XML)
Insert into #test VALUES(1,'
<GetOfferAvailabilityResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" ---GetOfferAvailabilityResponse>')
When I use Select #test.query ('\') I get the entire xml but when I try Select #test.query ('\GetOfferAvailabilityResponse') I receive an empty result
You can try something like this:
DECLARE #XmlTbl TABLE (ID INT, XMLDATA XML)
INSERT INTO #XmlTbl
( ID, XMLDATA )
VALUES ( 1, '<GetOfferAvailabilityResponse xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p3="http://somewebsite.com/v2.0" xmlns="http://somewebsite.com/v2.0" p3:TransactionID="281234567">
<p3:RuleResultList xsi:nil="true" />
<p3:ResultList>
<p3:ProviderResult p3:ProviderID="01" p3:ResultID="1234" p3:ResultType="NotAvailable" p3:ResultCode="NotAvailable" p3:BrokerID="55" p3:Structure="None">
<p3:EntityState>None</p3:EntityState>
<p3:ResultText>No Orders returned</p3:ResultText>
<p3:ShortDescription>Not Available</p3:ShortDescription>
<p3:LongDescription>We''re sorry, but offers are currently not available for your service address.</p3:LongDescription>
<p3:ResultAction>ErrorMessage</p3:ResultAction>
<p3:SourceResultCode xsi:nil="true" />
</p3:ProviderResult>
</p3:ResultList>
</GetOfferAvailabilityResponse>')
;WITH XMLNAMESPACES('http://somewebsite.com/v2.0' AS p3, DEFAULT 'http://somewebsite.com/v2.0')
SELECT
ProviderID = XmlData.value('(/GetOfferAvailabilityResponse/p3:ResultList/p3:ProviderResult/#p3:ProviderID)[1]', 'varchar(50)'),
EntityState = XmlData.value('(/GetOfferAvailabilityResponse/p3:ResultList/p3:ProviderResult/p3:EntityState)[1]', 'varchar(50)'),
ResultText = XmlData.value('(/GetOfferAvailabilityResponse/p3:ResultList/p3:ProviderResult/p3:ResultText)[1]', 'varchar(50)'),
ShortDescription = XmlData.value('(/GetOfferAvailabilityResponse/p3:ResultList/p3:ProviderResult/p3:ShortDescription)[1]', 'varchar(250)'),
LongDescription = XmlData.value('(/GetOfferAvailabilityResponse/p3:ResultList/p3:ProviderResult/p3:LongDescription)[1]', 'varchar(250)'),
ResultAction = XmlData.value('(/GetOfferAvailabilityResponse/p3:ResultList/p3:ProviderResult/p3:ResultAction)[1]', 'varchar(50)')
FROM
#XmlTbl
From a table that contains a column of type XML, select those bits and pieces that you need, taking into account the defined XML namespaces on your XML data
This gives me a result of: