Column as content with FOR XML PATH query in SQL Server - sql

I have a SQL query to get data from SQL Server 2012:
select dbo.TRIM(cfProj.cfProjId) as "cfProjId",
dbo.TRIM(cfProj.cfAcro) as "cfAcro",
(SELECT cfLangCode as "#cfLangCode", cfTrans as "#cfTrans", cfProjTitle.cfTitle
FROM dbo.cfProjTitle
WHERE dbo.cfProjTitle.cfProjId = dbo.cfProj.cfProjId
FOR XML PATH('cfTitle'), type)
from cfProj FOR XML PATH('cfProj')
This SQL returns data structure like:
<cfProj>
<cfProjId>00001111</cfProjId>
<cfAcro>222</cfAcro>
<cfTitle cfLangCode="ru" cfTrans="h">
<cfTitle>some title here</cfTitle>
</cfTitle>
</cfProj>
But I want get XML structure without second nested "cfTitle" element:
<cfProj>
<cfProjId>00001111</cfProjId>
<cfAcro>222</cfAcro>
<cfTitle cfLangCode="ru" cfTrans="h">some title here</cfTitle>
</cfProj>
Difference in this line: <cfTitle cfLangCode="ru" cfTrans="h">some title here</cfTitle>
Any ideas how I can get desirable result?

If you don't need multiple cfTitle elements, you can use marc_s solution, but if you do - you can try this:
select
p.cfProjId,
p.cfAcro,
(
select
pt.cfLangCode as [#cfLangCode],
pt.cfTrans as [#cfTrans],
pt.cfTitle as [text()]
from cfProjTitle as pt
where pt.cfProjId = p.cfProjId
for xml path('cfTitle'), type
)
from cfProj as p
for xml path('cfProj')
sql fiddle demo

Related

How to update an existing XML field data in SQL Server table?

I am trying to update CertificateIssuers field shown in the XML output below
Query:
SELECT Settings FROM Sites
Output:
<ClientOperationalSettings>
<Version>5.00.8740.1002</Version>
<SecurityConfiguration>
<SecurityModeMask>0</SecurityModeMask>
<SecurityModeMaskEx>480</SecurityModeMaskEx>
<HTTPPort>80</HTTPPort>
<HTTPSPort>443</HTTPSPort>
<CertificateStoreName/>
<CertificateIssuers>CN=Mars-M-CA-CA-1; DC=Mars; DC=Local</CertificateIssuers>
<CertificateSelectionCriteria>SubjectAttr:OU=London</CertificateSelectionCriteria>
<CertificateSelectFirstFlag>1</CertificateSelectFirstFlag>
<PKICertOptions>1</PKICertOptions>
<SiteSigningCert>308202ED308201D5A0030201020210129F196B3060FF94418BEA860E8DD712300D06092A864886F70D01010B05003016311430120603550403130B53697465205365727665723020170D3138303933303031353534395A180F32313138303930373031353534395A3016311430120603550403130B536974652053657276657230820122300D06092A864886F70D01010105000382010F003082010A0282010100B2B3E6757D8E4DF7977F061543CF629B9129C337D373945A4FFE4625D557370FA21B6E03C09AE692A32A6B783BE2C19E3FEC965054F9A54BFDDB7C4B23E45456353EEA9D14FCA5EF58DCDECA3EEB9B7D589162DD87005799D473BB1518398C638217986780CCE8DFB778A84915019700BAF53AB3205730060911D7EEF17C4EDE904953EAEB8BAEBC85CAAFE82F121FC16A9316DB09E94D76A01F61A2129F4F807ADCB68270410393D9BD2E435C253683B013677EFFEBF09EE6BAC25D9AA21742142A58A1E2EAF09BD75C07BA18B85080F6700245461734AB9A466B56BE7D7F2F8E07D62AA75253B8A2839777E7C0A6DC6FF575164232EB05B3C9EA2CFC3531C30203010001A3353033301B0603551D110414301282104D2D5030312E4D6172732E4C6F63616C30140603551D25040D300B06092B060104018237650B300D06092A864886F70D01010B050003820101004AAAD78752D9650694401B43191B0BD586B48A96990C68B40673444C878032CDB7E8113E7344E7FD47DF18FF0DE51F8396B5F670AA5998BE34E6F91BAA79593D5578FB54A3EDABA20E14FE4B5D2736E5FB234F4FED7BA53972A5462682B67A1B3DD6FD2599DBFF0DE49B99C44C1003932816784F06D47C69584DA61C9E026EB2B1730920A06B9296FE69141CCCA1677A52AF644E5834D429F4810340FB6FB30B061A3555F4C3E506B960496CF10E54145384B1A6FE87A1FB26EB078246D132DD509F82180B0CDD9AF485329159F2145CBA6B55A36777558F2901B30B9974739640C4B8DCDF0319C9A10EEF4EAA33B74E7F54CE5C6F313DCB4FF7EEA1FA6453A5</SiteSigningCert>
</SecurityConfiguration>
<AADConfig Version="1.0">
<Tenants/>
</AADConfig>
</ClientOperationalSettings>
I found XML from SQL column: Cannot call methods on nvarchar(max) and I am able to query the value like below
Query:
SELECT cast([Sites].[Settings] as xml).query('data(ClientOperationalSettings/SecurityConfiguration/CertificateIssuers)') FROM [dbo].[Sites]
Output:
(No column name)
CN=Mars-M-CA-CA-1; DC=Mars; DC=Local
but how to I update the CN to lets say to below for example
<CertificateIssuers>CN=Mars Planet; DC=Mars; DC=Local</CertificateIssuers>
You can try the following query.
UPDATE
dbo.Sites
SET
Settings.modify('replace value of (/ClientOperationalSettings/SecurityConfiguration/CertificateIssuers/text())[1] with "CN=Mars Planet; DC=Mars; DC=Local"')
WHERE ....

Parse XML field into table

I am trying to parse an XML field using SQL into a table and I need a little help starting. An example of the XML field for one row is as follows:
<MarketValueTransactionVo
objectId="104" statusCode="0" acctNum="60835733" recType="6"
errorFlag="N" sourceCode="0" userId="DATAEXCHANGE" taxItem="0"
amount="4496.79" accountEntityType="0" transactionAmount="4496.79"
importFormatType="5" dateEntered="01252015" clearingFirmBrokerCode="OPSX"
formattedAmount="$4,496.79" totalShares="0" controlNumberSequence="0"
applicableYear="2014" brokerCode="OPSX" ssn="632248334"
entityId="OPSX" entityTypeCode="4" activityApplicationCode="3001"
activityTypeCode="801" entityPresentationNumber="0" checkStatusCode="0"
correctionCode="0" correctionTypeCode="0" entityLOBCode="0"
requestPresentationNumber="0" requestStatusCode="0" reverseReasonCode="0"
loanPresentationNumber="0">
</MarketValueTransactionVo>
You want to address XML tag attributes. Tag attributes can be addressed using at-sign symbol #, see for examples Import XML with Attribute to SQL Server Table and Convert Xml to Table SQL Server (the second format in the answer):
SELECT
Tbl.Col.value('#objectId', 'int'),
Tbl.Col.value('#statusCode', 'tinyint'),
Tbl.Col.value('#acctNum', ...proper type int? varchar(xx)? ),
...
FROM #xml.nodes('//MarketValueTransactionVo') Tbl(Col)

Querying XML colum for values

I have a SQL Server table with an XML column, and it contains data something like this:
<Query>
<QueryGroup>
<QueryRule>
<Attribute>Integration</Attribute>
<RuleOperator>8</RuleOperator>
<Value />
<Grouping>OrOperator</Grouping>
</QueryRule>
<QueryRule>
<Attribute>Integration</Attribute>
<RuleOperator>5</RuleOperator>
<Value>None</Value>
<Grouping>AndOperator</Grouping>
</QueryRule>
</QueryGroup>
</Query>
Each QueryRule will only have one Attribute, but each QueryGroup can have many QueryRules. Each Query can also have many QueryGroups.
I need to be able to pull all records that have one or more QueryRule with a certain attribute and value.
SELECT *
FROM QueryBuilderQueries
WHERE [the xml contains any value=X where the attribute is either Y or Z]
I've worked out how to check a specific QueryRule, but not "any".
SELECT
Query
FROM
QueryBuilderQueries
WHERE
Query.value('(/Query/QueryGroup/QueryRule/Value)[1]', 'varchar(max)') like 'UserToFind'
AND Query.value('(/Query/QueryGroup/QueryRule/Attribute)[1]', 'varchar(max)') in ('FirstName', 'LastName')
You can use two exist(). One to check the value and one to check Attribute.
select Q.Query
from dbo.QueryBuilderQueries as Q
where Q.Query.exist('/Query/QueryGroup/QueryRule/Value/text()[. = "UserToFind"]') = 1 and
Q.Query.exist('/Query/QueryGroup/QueryRule/Attribute/text()[. = ("FirstName", "LastName")]') = 1
If you really want the like equivalence when you search for a Value you can use contains().
select Q.Query
from dbo.QueryBuilderQueries as Q
where Q.Query.exist('/Query/QueryGroup/QueryRule/Value/text()[contains(., "UserToFind")]') = 1 and
Q.Query.exist('/Query/QueryGroup/QueryRule/Attribute/text()[. = ("FirstName", "LastName")]') = 1
According to http://technet.microsoft.com/pl-pl/library/ms178030%28v=sql.110%29.aspx
"The XQuery must return at most one value"
If you are quite certain that for example your XML has let's say maximum 10 QueryRules you could maybe use WHILE to loop everything while droping your results into temporary table?
maybe below can help you anyway
CREATE TABLE #temp(
Query type)
DECLARE #i INT
SET #i = 1
WHILE #i >= 10
BEGIN
INSERT INTO #temp
SELECT
Query
FROM QueryBuilderQueries
WHERE Query.value('(/Query/QueryGroup/QueryRule/Value)[#i]', 'varchar(max)') LIKE 'UserToFind'
AND Query.value('(/Query/QueryGroup/QueryRule/Attribute)[#i]', 'varchar(max)') IN ('FirstName', 'LastName')
#i = #i + 1
END
SELECT
*
FROM #temp
It's a pity that the SQL Server (I'm using 2008) does not support some XQuery functions related to string such as fn:matches, ... If it supported such functions, we could query right inside XQuery expression to determine if there is any. However we still have another approach. That is by turning all the possible values into the corresponding SQL row to use the WHERE and LIKE features of SQL for searching/filtering. After some experiementing with the nodes() method (used on an XML data), I think it's the best choice to go:
select *
from QueryBuilderQueries
where exists( select *
from Query.nodes('//QueryRule') as v(x)
where LOWER(v.x.value('(Attribute)[1]','varchar(max)'))
in ('firstname','lastname')
and v.x.value('(Value)[1]','varchar(max)') like 'UserToFind')

Combine 2 XML Output Statements into one XML file

I've been working on outputting from SQL to XML and have finally come up with 2 queries that work fabulously for what I need with one problem. I did these independently as I am a novice with XML and now I need to combine them into one file for output. What I have is pretty complicated using dynamic SQL so I'm just going to post the dynamic part and exclude all of the variable and un-pivoting stuff I'm doing at the beginning. If I could just get result set 1 to be at the top and result set 2 just below it would suffice. I would like a wrapper tag (not sure if that's the term) as the very first and very last lines in this file. I'm not sure if anyone can tell me what I'm doing wrong just by looking at the dynamic code but I thought it would be worth a try. As I said, both of these work great but produce 2 separate files. Thanks in advance!
EXEC ('
SELECT
(SELECT GETDATE() AS STARTDATE,
GETDATE() AS ENDDATE
FOR XML PATH(''RECDATE''),ELEMENTS, TYPE),
(SELECT COUNT(*) AS SURVEY_COUNT
FROM PM_TEMP.dbo.tmpKansasCancerCenterExtract FOR XML PATH(''''), ELEMENTS, TYPE),
(SELECT ''PEPM'' as SERVICE,
DataCol as VARNAME,
Question as QUESTION_TEXT,
AnswerValue as ANSWER_TEXT
from PM_TEMP.dbo.tmpKansasCancerCenterCodeSheetExtract FOR XML PATH(''QUESTION''), ROOT(''QUESTION_MAP''), ELEMENTS, TYPE)
FOR XML PATH(''HEADER'')
SELECT PATID,CLIENTID,SERVICE,PATVISITDT,DATE,
(
SELECT VarName,Value
FROM PM_TEMP.dbo.tmpKansasCancerCenterExtract_AnalysisPivot P
INNER JOIN PM_TEMP.dbo.tmpKansasCancerCenterExtract E ON E.PatVisitID = P.PatVisitID
FOR XML PATH(''Response''), TYPE, ROOT(''Analysis'')
),
(
SELECT VarName,Value
FROM PM_TEMP.dbo.tmpKansasCancerCenterExtract_DemoPivot P
INNER JOIN PM_TEMP.dbo.tmpKansasCancerCenterExtract E ON E.PatVisitID = P.PatVisitID
FOR XML PATH(''Response''), TYPE, ROOT(''Demographics'')
)
FROM PM_TEMP.dbo.tmpKansasCancerCenterExtract
FOR XML PATH(''PatientLevelData''),TYPE, ROOT(''PatientLevelData'')
')

Using Fields[0].Value to get XML from FOR XML RAW, ELEMENTS query is messed up

I have a query that uses FOR XML RAW, ELEMENTS to return a SELECT query as a structured XML document. However, when I get the result using a TSQLDataSet by using Fields[0].Value, the result is different from what I see when I run the query in SQL Server Management Studio.
What I see in the result from the TSQLDataSet:
੄customerIdфname၄governmentNumberไdebtorAddress1ไdebtorAddress2ไdebtorAddress3ไdebtorAddress4ࡄpostCodeୄcontactNameՄphonë́faxൄcustomerSinceՄtermsلactiveไcurrentBalanceلDebtorခŁ䄁ഃӤ
What I see in the result in SSMS:
<Debtor>
<customerId>C0E449E5B2C </customerId>
<name>New Customer 2 </name>
<governmentNumber> </governmentNumber>
<debtorAddress1>Address Line 1 </debtorAddress1>
<debtorAddress4>Address Line 4 </debtorAddress4>
<postCode>1234 </postCode>
<phone>1234567890 </phone>
<fax>1234567890 </fax>
<customerSince>2013-06-10T18:16:06.213</customerSince>
<terms>M </terms>
<active>true</active>
<currentBalance>0.0000</currentBalance>
</Debtor>
Is there a particular way it should be executed to get the right result?
AFAIK this is a DbExpress limitation. I know how overcome this, but using ADO (the returned data must be requested using a special parametrized object and a set of ADO streams). However you can use a workaround converting the XML data to a string in the server side sorrounding the sentence with a select (subquery) or just using a simple CAST statement.
For example if you sentence is like so
SELECT Foo, Bar FROM FooTable FOR XML RAW, ELEMENTS
you can rewrite to
SELECT (SELECT Foo, Bar FROM FooTable FOR XML RAW, ELEMENTS)
or you can rewrite to (use a CAST VARCHAR or NVARCHAR)
SELECT CAST( (SELECT Foo, Bar FROM FooTable FOR XML RAW, ELEMENTS) AS VARCHAR(MAX))
and finally
Retrieve the result like this
SQLDataSet1.Fields[0].AsString