I have a huge XML, with over 150 element and attributes. I need to populate only a handful of them ( 5 or 6) elements with data from a SQL server 2008 tables. This created xml need to be passed to a webservice ( to ESB) and this has to happen on a daily schedule.
I think, it does not make sense to use XML Path here to create the XML and use SSIS. Can anyone suggest an approach?
This is the sample XML. I need just the names, address and a few ids populated
<?xml version="1.0" encoding="UTF-8"?>
<Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Customer.xsd">
<CustomerData>
<StoreID></StoreID>
<FirstName></FirstName>
<LastName></LastName>
<Gender></Gender>
<BirthMonth></BirthMonth>
<BirthYear></BirthYear>
<BirthDay></BirthDay>
<IsEmployee></IsEmployee>
<customerID></customerID>
<EmailAddress></EmailAddress>
<PhoneNumber></PhoneNumber>
<EmailSignUp></EmailSignUp>
<CatalogSignup></CatalogSignup>
<Addresses>
<BillingAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</BillingAddress>
<CatalogSignupAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</CatalogSignupAddress>
<StoredAddresses>
<StoredAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</StoredAddress>
<StoredAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</StoredAddress>
</StoredAddresses>
</Addresses>
<Hobbies_Enjoys>
<Hobby></Hobby>
<Enjoy></Enjoy>
</Hobbies_Enjoys>
<FavoriteFossilProducts>
<ProductType></ProductType>
<ProductType></ProductType>
</FavoriteFossilProducts>
<AgeRange></AgeRange>
<PreferredEmailFormat></PreferredEmailFormat>
<SavedCreditCards>
<CreditCard>
<AcctNum></AcctNum>
<ExpiryDate></ExpiryDate>
</CreditCard>
<CreditCard>
<AcctNum></AcctNum>
<ExpiryDate></ExpiryDate>
</CreditCard>
</SavedCreditCards>
<PurchasedProductList>
<SKU></SKU>
<SKU></SKU>
</PurchasedProductList>
<OrderHistory>
<Orders>
<Order>
<Number></Number>
<TotalAmount></TotalAmount>
<Promotion></Promotion>
<TimePlaced></TimePlaced>
</Order>
<Order>
<Number></Number>
<TotalAmount></TotalAmount>
<Promotion></Promotion>
<TimePlaced></TimePlaced>
</Order>
</Orders>
</OrderHistory>
<WishLists>
<WishList>
<Name></Name>
<Description></Description>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
<IsDefault></IsDefault>
<Items>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
</Items>
</WishList>
<WishList>
<Name></Name>
<Description></Description>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
<IsDefault></IsDefault>
<Items>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
</Items>
</WishList>
</WishLists>
</CustomerData>
<CustomerData>
</CustomerData>
</Customer>
One approach is to take an empty version of your XML document and assign it to an NVARCHAR(MAX) variable. For each node or parameter you wish to populate, put a "token" and use the replace function to populate the value. For example:
DECLARE #xml NVARCHAR(MAX)
SET #xml = N'<Company name="~company_name~">
<Department name="~dept_name~">
<Employee name="~employee_name~" title="~employee_title~" />
</Department>
</Company>'
SET #xml = REPLACE(#xml,N'~company_name~','Microsoft')
SET #xml = REPLACE(#xml,N'~dept_name~','Executive')
SET #xml = REPLACE(#xml,N'~employee_name~','Satya Nadella')
SET #xml = REPLACE(#xml,N'~employee_title~','CEO')
SELECT CAST(#xml AS XML)
Here's a more traditional approach. You'll need an arbitrary value in the cells you wish to update:
DECLARE #example_data TABLE
(FirstName VARCHAR(255) NOT NULL,
LastName VARCHAR(255) NOT NULL,
Gender CHAR(1) NOT NULL,
City VARCHAR(255) NOT NULL,
[State] CHAR(2) NOT NULL)
INSERT INTO #example_data
( FirstName , LastName , Gender , City , [State])
VALUES ( 'Satya' , -- FirstName - varchar(255)
'Nadella' , -- LastName - varchar(255)
'M' , -- Gender - char(1)
'Redmond' , -- City - varchar(255)
'WA' -- State - char(2)
)
INSERT INTO #example_data
( FirstName , LastName , Gender , City , [State])
VALUES ( 'Larry' , -- FirstName - varchar(255)
'Ellison' , -- LastName - varchar(255)
'M' , -- Gender - char(1)
'Woodside' , -- City - varchar(255)
'CA' -- State - char(2)
)
DECLARE
#customer_xml XML,
#xml XML,
#temp NVARCHAR(255),
#FirstName VARCHAR(255),
#LastName VARCHAR(255),
#Gender CHAR(1),
#City VARCHAR(255),
#State CHAR(2)
SET #customer_xml = '<?xml version="1.0" encoding="UTF-8"?>
<Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Customer.xsd">
<CustomerData>
<StoreID></StoreID>
<FirstName>a</FirstName>
<LastName>b</LastName>
<Gender>c</Gender>
<BirthMonth></BirthMonth>
<BirthYear></BirthYear>
<BirthDay></BirthDay>
<IsEmployee></IsEmployee>
<customerID></customerID>
<EmailAddress></EmailAddress>
<PhoneNumber></PhoneNumber>
<EmailSignUp></EmailSignUp>
<CatalogSignup></CatalogSignup>
<Addresses>
<BillingAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City>d</City>
<State>e</State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</BillingAddress>
<CatalogSignupAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</CatalogSignupAddress>
<StoredAddresses>
<StoredAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</StoredAddress>
<StoredAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</StoredAddress>
</StoredAddresses>
</Addresses>
<Hobbies_Enjoys>
<Hobby></Hobby>
<Enjoy></Enjoy>
</Hobbies_Enjoys>
<FavoriteFossilProducts>
<ProductType></ProductType>
<ProductType></ProductType>
</FavoriteFossilProducts>
<AgeRange></AgeRange>
<PreferredEmailFormat></PreferredEmailFormat>
<SavedCreditCards>
<CreditCard>
<AcctNum></AcctNum>
<ExpiryDate></ExpiryDate>
</CreditCard>
<CreditCard>
<AcctNum></AcctNum>
<ExpiryDate></ExpiryDate>
</CreditCard>
</SavedCreditCards>
<PurchasedProductList>
<SKU></SKU>
<SKU></SKU>
</PurchasedProductList>
<OrderHistory>
<Orders>
<Order>
<Number></Number>
<TotalAmount></TotalAmount>
<Promotion></Promotion>
<TimePlaced></TimePlaced>
</Order>
<Order>
<Number></Number>
<TotalAmount></TotalAmount>
<Promotion></Promotion>
<TimePlaced></TimePlaced>
</Order>
</Orders>
</OrderHistory>
<WishLists>
<WishList>
<Name></Name>
<Description></Description>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
<IsDefault></IsDefault>
<Items>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
</Items>
</WishList>
<WishList>
<Name></Name>
<Description></Description>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
<IsDefault></IsDefault>
<Items>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
</Items>
</WishList>
</WishLists>
</CustomerData>
</Customer>'
DECLARE xml_cursor CURSOR FOR
SELECT FirstName , LastName , Gender , City , [State]
FROM #example_data
OPEN xml_cursor
FETCH NEXT FROM xml_cursor into #FirstName, #LastName, #Gender, #City, #State
WHILE ##fetch_status = 0
BEGIN
SET #xml = #customer_xml
SET #xml.modify('
replace value of (/Customer/CustomerData/FirstName[1]/text())[1]
with sql:variable("#FirstName")
')
SET #xml.modify('
replace value of (/Customer/CustomerData/LastName[1]/text())[1]
with sql:variable("#LastName")
')
SET #xml.modify('
replace value of (/Customer/CustomerData/Gender[1]/text())[1]
with sql:variable("#Gender")
')
SET #xml.modify('
replace value of (/Customer/CustomerData/Addresses/BillingAddress/City[1]/text())[1]
with sql:variable("#City")
')
SET #xml.modify('
replace value of (/Customer/CustomerData/Addresses/BillingAddress/State[1]/text())[1]
with sql:variable("#State")
')
select #xml
FETCH NEXT FROM xml_cursor into #FirstName, #LastName, #Gender, #City, #State
END
This approach does not require a value in the nodes to be updated. It simply inserts a new node, then deletes the old node:
DECLARE #example_data TABLE
(FirstName VARCHAR(255) NOT NULL,
LastName VARCHAR(255) NOT NULL,
Gender CHAR(1) NOT NULL,
City VARCHAR(255) NOT NULL,
[State] CHAR(2) NOT NULL)
INSERT INTO #example_data
( FirstName , LastName , Gender , City , [State])
VALUES ( 'Satya' , -- FirstName - varchar(255)
'Nadella' , -- LastName - varchar(255)
'M' , -- Gender - char(1)
'Redmond' , -- City - varchar(255)
'WA' -- State - char(2)
)
INSERT INTO #example_data
( FirstName , LastName , Gender , City , [State])
VALUES ( 'Larry' , -- FirstName - varchar(255)
'Ellison' , -- LastName - varchar(255)
'M' , -- Gender - char(1)
'Woodside' , -- City - varchar(255)
'CA' -- State - char(2)
)
DECLARE
#customer_xml XML,
#xml XML,
#temp NVARCHAR(255),
#FirstName VARCHAR(255),
#LastName VARCHAR(255),
#Gender CHAR(1),
#City VARCHAR(255),
#State CHAR(2)
SET #customer_xml = '<?xml version="1.0" encoding="UTF-8"?>
<Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Customer.xsd">
<CustomerData>
<StoreID></StoreID>
<FirstName></FirstName>
<LastName></LastName>
<Gender></Gender>
<BirthMonth></BirthMonth>
<BirthYear></BirthYear>
<BirthDay></BirthDay>
<IsEmployee></IsEmployee>
<customerID></customerID>
<EmailAddress></EmailAddress>
<PhoneNumber></PhoneNumber>
<EmailSignUp></EmailSignUp>
<CatalogSignup></CatalogSignup>
<Addresses>
<BillingAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</BillingAddress>
<CatalogSignupAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</CatalogSignupAddress>
<StoredAddresses>
<StoredAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</StoredAddress>
<StoredAddress>
<Address1></Address1>
<Address2></Address2>
<Address3></Address3>
<City></City>
<State></State>
<ZipCode></ZipCode>
<Fax1></Fax1>
</StoredAddress>
</StoredAddresses>
</Addresses>
<Hobbies_Enjoys>
<Hobby></Hobby>
<Enjoy></Enjoy>
</Hobbies_Enjoys>
<FavoriteFossilProducts>
<ProductType></ProductType>
<ProductType></ProductType>
</FavoriteFossilProducts>
<AgeRange></AgeRange>
<PreferredEmailFormat></PreferredEmailFormat>
<SavedCreditCards>
<CreditCard>
<AcctNum></AcctNum>
<ExpiryDate></ExpiryDate>
</CreditCard>
<CreditCard>
<AcctNum></AcctNum>
<ExpiryDate></ExpiryDate>
</CreditCard>
</SavedCreditCards>
<PurchasedProductList>
<SKU></SKU>
<SKU></SKU>
</PurchasedProductList>
<OrderHistory>
<Orders>
<Order>
<Number></Number>
<TotalAmount></TotalAmount>
<Promotion></Promotion>
<TimePlaced></TimePlaced>
</Order>
<Order>
<Number></Number>
<TotalAmount></TotalAmount>
<Promotion></Promotion>
<TimePlaced></TimePlaced>
</Order>
</Orders>
</OrderHistory>
<WishLists>
<WishList>
<Name></Name>
<Description></Description>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
<IsDefault></IsDefault>
<Items>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
</Items>
</WishList>
<WishList>
<Name></Name>
<Description></Description>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
<IsDefault></IsDefault>
<Items>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
<Item>
<Quantity></Quantity>
<PartNumber></PartNumber>
<DateCreated></DateCreated>
<LastUpdate></LastUpdate>
</Item>
</Items>
</WishList>
</WishLists>
</CustomerData>
</Customer>'
DECLARE xml_cursor CURSOR FOR
SELECT FirstName , LastName , Gender , City , [State]
FROM #example_data
OPEN xml_cursor
FETCH NEXT FROM xml_cursor into #FirstName, #LastName, #Gender, #City, #State
WHILE ##fetch_status = 0
BEGIN
SET #xml = #customer_xml
SET #xml.modify('
insert <FirstName>{ xs:string(sql:variable("#FirstName")) }</FirstName>
before (/Customer/CustomerData/FirstName)[1]
')
SET #xml.modify('
delete /Customer/CustomerData/FirstName[2]
')
SET #xml.modify('
insert <LastName>{ xs:string(sql:variable("#LastName")) }</LastName>
before (/Customer/CustomerData/LastName)[1]
')
SET #xml.modify('
delete /Customer/CustomerData/LastName[2]
')
SET #xml.modify('
insert <Gender>{ xs:string(sql:variable("#Gender")) }</Gender>
before (/Customer/CustomerData/Gender)[1]
')
SET #xml.modify('
delete /Customer/CustomerData/Gender[2]
')
SET #xml.modify('
insert <City>{ xs:string(sql:variable("#City")) }</City>
before (/Customer/CustomerData/Addresses/BillingAddress/City)[1]
')
SET #xml.modify('
delete /Customer/CustomerData/Addresses/BillingAddress/City[2]
')
SET #xml.modify('
insert <State>{ xs:string(sql:variable("#State")) }</State>
before (/Customer/CustomerData/Addresses/BillingAddress/State)[1]
')
SET #xml.modify('
delete /Customer/CustomerData/Addresses/BillingAddress/State[2]
')
select #xml
FETCH NEXT FROM xml_cursor into #FirstName, #LastName, #Gender, #City, #State
END
you can also use FLWOR which will allow you to generate the xml in one go by using a template and adding data only to the elements you want.
You won't need to create tables or stored procedures. For example:
declare #x xml
set #x=(select FirstName, LastName from kc_consumer for xml path('CustomerData'))
SELECT #x.query('
for $a in /CustomerData
return
<CustomerData>
<StoreID></StoreID>
<FirstName>{data($a/FirstName)}</FirstName>
<LastName>{data($a/LastName)}</LastName>
<Gender></Gender>
<BirthMonth></BirthMonth>
<BirthYear></BirthYear>
<BirthDay></BirthDay>
<IsEmployee></IsEmployee>
<customerID></customerID>
</CustomerData>
')
I'm just including a few nodes for readability, you just need to paste the rest of them.
Related
I would like to be able to extract specific data from a XML type using Oracle in my example for the customer named "Arshad Ali"
This is my xml data that was inserted:
<Customers>
<Customer CustomerName="Arshad Ali" CustomerID="C001">
<Orders>
<Order OrderDate="2012-07-04T00:00:00" OrderID="10248">
<OrderDetail Quantity="5" ProductID="10" />
<OrderDetail Quantity="12" ProductID="11" />
<OrderDetail Quantity="10" ProductID="42" />
</Order>
</Orders>
<Address> Address line 1, 2, 3</Address>
</Customer>
<Customer CustomerName="Paul Henriot" CustomerID="C002">
<Orders>
<Order OrderDate="2011-07-04T00:00:00" OrderID="10245">
<OrderDetail Quantity="12" ProductID="11" />
<OrderDetail Quantity="10" ProductID="42" />
</Order>
</Orders>
<Address> Address line 5, 6, 7</Address>
</Customer>
<Customer CustomerName="Carlos Gonzlez" CustomerID="C003">
<Orders>
<Order OrderDate="2012-08-16T00:00:00" OrderID="10283">
<OrderDetail Quantity="3" ProductID="72" />
</Order>
</Orders>
<Address> Address line 1, 4, 5</Address>
</Customer>
</Customers>
</ROOT>
using get clob I was able to extract all of the customers.
Was wondering if anyone could help me extract data for a specific customer.. tried using the following but was unsuccessful
SELECT extract(OBJECT_VALUE, '/root/Customers') "customer"
FROM mytable2
WHERE existsNode(OBJECT_VALUE, '/customers[CustomerName="Arshad Ali" CustomerID="C001"]')
= 1;
The case and exact names of the XML nodes matter:
SELECT extract(OBJECT_VALUE,
'/ROOT/Customers/Customer[#CustomerName="Arshad Ali"][#CustomerID="C001"]') "customer"
FROM mytable2
WHERE existsnode (OBJECT_VALUE,
'/ROOT/Customers/Customer[#CustomerName="Arshad Ali"][#CustomerID="C001"]') = 1
db<>fiddle
If you only want to search by name then only use that attribute:
SELECT extract(OBJECT_VALUE,
'/ROOT/Customers/Customer[#CustomerName="Arshad Ali"]') "customer"
FROM mytable2
WHERE existsnode (OBJECT_VALUE,
'/ROOT/Customers/Customer[#CustomerName="Arshad Ali"]') = 1
But extract() and existsnode() are deprecated; use xmlquery() and xmlexists() instead:
SELECT xmlquery('/ROOT/Customers/Customer[#CustomerName="Arshad Ali"][#CustomerID="C001"]'
passing object_value
returning content) "customer"
FROM mytable2
WHERE xmlexists('/ROOT/Customers/Customer[#CustomerName="Arshad Ali"][#CustomerID="C001"]'
passing object_value)
db<>fiddle
I am trying to group some elements together under one node. This is my current SQL;
declare #xml xml
set #xml = (
select (
select
'DERIVED' '#type',
m.NuixDerivedFieldName '#name', (
SELECT
NuixFieldType as 'metadata/#type',
NuixFieldName as 'metadata/#name'
from eddsdbo.MetadataMapping m1
where m1.NuixDerivedFieldName = m.NuixDerivedFieldName
for xml path ('first-non-blank'), type
)
from (select distinct NuixDerivedFieldName from eddsdbo.MetadataMapping) m
for xml path ('metadata'))
)
;WITH XMLNAMESPACES(DEFAULT 'http://nuix.com/fbi/metadata-profile')
select #xml for XML PATH ('metadata-list'), ROOT ('metadata-profile')
Which gives me the following output;
<metadata-profile xmlns="http://nuix.com/fbi/metadata-profile">
<metadata-list>
<metadata type="DERIVED" name="Barcode" xmlns="">
<first-non-blank>
<metadata type="CUSTOM" name="Barcode" />
</first-non-blank>
<first-non-blank>
<metadata type="EVIDENCE" name="Barcode" />
</first-non-blank>
</metadata>
I want to group together elements together which have the same 'name' attribute of the metadata element under the <first-non-blank> element.
The desired output should be;
<metadata-profile xmlns="http://nuix.com/fbi/metadata-profile">
<metadata-list>
<metadata type="DERIVED" name="Barcode" xmlns="">
<first-non-blank>
<metadata type="CUSTOM" name="Barcode" />
<metadata type="EVIDENCE" name="Barcode" />
</first-non-blank>
</metadata>
...
My database looks something like this;
NuixFieldName NuixFieldType NuixDerivedFieldName
------------------------------ ------------------------------ ------------------------------
_EmailEntryID PROPERTY EmailEntryID
Audited Audited Audited
Author PROPERTY Author
Barcode CUSTOM Barcode
Barcode EVIDENCE Barcode
I would also like to remove the xlmns namespace identifier from the metadata elements.
Thanks in advance!
You could try this
DECLARE #SampleData AS TABLE
(
NuixFieldName varchar(20),
NuixFieldType varchar(20),
NuixDerivedFieldName varchar(20)
)
INSERT INTO #SampleData
VALUES
('_EmailEntryID','PROPERTY','EmailEntryID'),
('Audited', 'Audited ','Audited'),
('Author ', 'PROPERTY','Author '),
('Barcode', 'CUSTOM ','Barcode'),
('Barcode', 'EVIDENCE','Barcode')
DECLARE #xml XML
SET #xml = (
SELECT
-- sd.NuixDerivedFieldName AS [#name],
'DERIVED' AS [#type],
sd.NuixDerivedFieldName AS [#name],
(
SELECT
sd2.NuixFieldType as '#type',
sd2.NuixFieldName as '#name'
FROM #SampleData sd2 WHERE sd2.NuixDerivedFieldName = sd.NuixDerivedFieldName
FOR XML PATH ('metadata'),ROOT('first-non-blank'), TYPE
)
FROM (select DISTINCT sd.NuixDerivedFieldName from #SampleData sd ) sd
FOR XML PATH('metadata'), ROOT('metadata-list'),TYPE
)
;WITH XMLNAMESPACES(DEFAULT 'http://nuix.com/fbi/metadata-profile')
SELECT #xml FOR XML PATH (''),ROOT('metadata-profile')
return:
<metadata-profile xmlns="http://nuix.com/fbi/metadata-profile">
<metadata-list>
<metadata type="DERIVED" name="Audited">
<first-non-blank>
<metadata type="Audited " name="Audited" />
</first-non-blank>
</metadata>
<metadata type="DERIVED" name="Author ">
<first-non-blank>
<metadata type="PROPERTY" name="Author " />
</first-non-blank>
</metadata>
<metadata type="DERIVED" name="Barcode">
<first-non-blank>
<metadata type="CUSTOM " name="Barcode" />
<metadata type="EVIDENCE" name="Barcode" />
</first-non-blank>
</metadata>
<metadata type="DERIVED" name="EmailEntryID">
<first-non-blank>
<metadata type="PROPERTY" name="_EmailEntryID" />
</first-non-blank>
</metadata>
</metadata-list>
</metadata-profile>
I am calling Scalar UDF from a stored procedure to get a column value. Inside the scalar UDF I have an xml and I have to get the comma separated values of a particular node. I used Cross apply but it caused huge performance bottleneck because stored procedure is actually used to fetch reports.
There is a table [Traveler] which has a field ID, BookingID(can be duplicate) and FareDetails. Inside the FareDetails we are storing the xml.
The logic inside UDF is as follows :
1st Solution , Using Cross Apply :
ALTER FUNCTION [dbo].[GetBookingInfo] (#BookingID bigint, #InfoID smallint) RETURNS VARCHAR(1024) AS
BEGIN
DECLARE #InfoCSV VARCHAR(1024)
--
-- Fare Basis: InfoID = 1
--
IF #InfoID = 1
BEGIN
SELECT #InfoCSV = (SELECT
(PTSD.PSTDNode.value('(FBC)[1]', 'VARCHAR(1024)') + ',') [text()]
FROM
[Traveler]
CROSS APPLY [FareDetails].nodes('/AirFareInfo/PTSDPFS/PTSD') PTSD(PSTDNode)
WHERE
[BookingID] = #BookingID
ORDER BY
ID ASC
FOR XML PATH (''))
IF #InfoCSV IS NOT NULL AND LEN(#InfoCSV) > 0
SET #InfoCSV = LEFT(#InfoCSV, LEN(#InfoCSV) - 1)
END
RETURN #InfoCSV
2nd solution , without cross apply :
ALTER FUNCTION [dbo].[GetBookingInfo] (#BookingID bigint, #InfoID smallint) RETURNS VARCHAR(1024) AS
BEGIN
DECLARE #InfoCSV VARCHAR(1024)
--
-- Fare Basis: InfoID = 1
--
IF #InfoID = 1
BEGIN
SELECT #InfoCSV = (SELECT TOP 1 REPLACE(FareDetails.query(N'data(/AirFareInfo/PTSDPFS/PTSD/FBC)').value('(text())[1]','nvarchar(100)'),' ',',')
FROM [Traveler]
WHERE
[BookingID] = #BookingID)
IF #InfoCSV IS NOT NULL AND LEN(#InfoCSV) > 0
SET #InfoCSV = LEFT(#InfoCSV, LEN(#InfoCSV) - 1)
END
RETURN #InfoCSV
The 2nd solution is saving lot of time , but when we have duplicate booking ID's then it is not concatenating all the values of FBC .
e.g. :
1)If BookingID is unique and we have FareDetail xml as follows then output should be be AP,AP
2)If BookingID is not unique(coming twice) and we have FareDetail xml as follows then output should be be AP,AP,AP,AP corresponding to both BookingID.
The xml is as follows :
<AirFareInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IPFA="false">
<PT>Flight</PT>
<FPMID>0</FPMID>
<PTID>1</PTID>
<FS>
<CID>2</CID>
<Value>0</Value>
</FS>
<TF>
<CID xsi:nil="true" />
<Value>0</Value>
</TF>
<VF>
<CID>2</CID>
<Value>0</Value>
</VF>
<VD>
<CID>2</CID>
<Value>0</Value>
</VD>
<VCR xsi:nil="true" />
<VC>
<CID>2</CID>
<Value>0</Value>
</VC>
<VFC>
<CID>2</CID>
<Value>0</Value>
</VFC>
<VST />
<VIT />
<AAPFVDR xsi:nil="true" />
<CC>
<CID>2</CID>
<Value>0</Value>
</CC>
<D>
<CID>2</CID>
<Value>514.15</Value>
</D>
<PD>
<CID>2</CID>
<Value>0</Value>
</PD>
<EBF>
<CID>2</CID>
<Value>0</Value>
</EBF>
<CST>
<DL>
<ATRID>13</ATRID>
<OB>
<CID>2</CID>
<Value>74.04</Value>
</OB>
<OC>
<CID>2</CID>
<Value>0.00</Value>
</OC>
<OS>
<CID>2</CID>
<Value>0.00</Value>
</OS>
<OF>
<CID>2</CID>
<Value>50.83</Value>
</OF>
<OP>
<CID>2</CID>
<Value>0.00</Value>
</OP>
<C>
<CID>2</CID>
<Value>0</Value>
</C>
<IBF>false</IBF>
<D>2014-06-09T14:57:53.521Z</D>
</DL>
</CST>
<CIT />
<CRMR xsi:nil="true" />
<CRM>
<CID>2</CID>
<Value>0</Value>
</CRM>
<TL ATC="Tax" PC="" DEN="User Development Fee - Arrival (UDF)">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>75.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Passenger Service Fee">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>146.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="User Development Fee - Departure (UDF)">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>1681.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Cute Fee">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>50.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Government Service Tax">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>151.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="User Development Fee - Arrival (UDF)">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>833.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Passenger Service Fee">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>1132.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="User Development Fee - Departure (UDF)">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>76.00</Value>
</Amount>
</TL>
<TL ATC="Tax" PC="" DEN="Government Service Tax">
<TID xsi:nil="true" />
<Amount>
<CID>2</CID>
<Value>148.00</Value>
</Amount>
</TL>
<PTSDPFS>
<PTSD IO="false">
<FBC>AP</FBC>
<ACD RBD="" ACCID="1" MCT="Super Sale Fare(AP)" INC="false" />
<ATSID xsi:nil="true" />
</PTSD>
</PTSDPFS>
<PTSDPFS>
<PTSD IO="false">
<FBC>AP</FBC>
<ACD RBD="" ACCID="1" MCT="Super Sale Fare(AP)" INC="false" />
<ATSID xsi:nil="true" />
</PTSD>
</PTSDPFS>
<RuleDetails>
<TRS xsi:nil="true" />
<PP xsi:nil="true" />
<II xsi:nil="true" />
<LTD xsi:nil="true" />
</RuleDetails>
</AirFareInfo>
Please suggest how it can be done keeping performance in mind.
This is a fully working example.
You told us, that performance matters, so do not use scalar UDF!
Try it like this (next time it's your job to create a (reduced!!!) MCVE:
CREATE DATABASE testDB;
GO
USE testDB;
GO
CREATE TABLE Booking(BookingID INT CONSTRAINT PK_Booking PRIMARY KEY
,SomeBookingData VARCHAR(100));
INSERT INTO Booking VALUES(1,'Booking 1'),(2,'Booking 2');
CREATE TABLE BookingInfo(BookingID INT CONSTRAINT FK_BookingInfo_BookingID FOREIGN KEY REFERENCES Booking(BookingID)
,SomeOtherInfo VARCHAR(100)
,FareDetails XML);
INSERT INTO BookingInfo VALUES
(1,'First row for ID=1, returns AP,AP'
,N'<AirFareInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IPFA="false">
<PTSDPFS>
<PTSD IO="false">
<FBC>AP</FBC>
</PTSD>
</PTSDPFS>
<PTSDPFS>
<PTSD IO="false">
<FBC>AP</FBC>
</PTSD>
</PTSDPFS>
</AirFareInfo>')
,(1,'Second row for ID=1, returns XY,MN'
,N'<AirFareInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IPFA="false">
<PTSDPFS>
<PTSD IO="false">
<FBC>XY</FBC>
</PTSD>
</PTSDPFS>
<PTSDPFS>
<PTSD IO="false">
<FBC>MN</FBC>
</PTSD>
</PTSDPFS>
</AirFareInfo>')
,(2,'row with ID=2, returns AA,BB'
,N'<AirFareInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IPFA="false">
<PTSDPFS>
<PTSD IO="false">
<FBC>AA</FBC>
</PTSD>
</PTSDPFS>
<PTSDPFS>
<PTSD IO="false">
<FBC>BB</FBC>
</PTSD>
</PTSDPFS>
</AirFareInfo>');
GO
--This is the function. It returns as table and is fully inlined (no BEGIN...END!)
CREATE FUNCTION dbo.CreateBookingInfoCSV(#BookingID INT)
RETURNS TABLE
AS
RETURN
SELECT STUFF(
(
SELECT ','+REPLACE(FareDetails.query(N'data(/AirFareInfo/PTSDPFS/PTSD/FBC)').value(N'.',N'nvarchar(max)'),' ',',')
FROM BookingInfo AS bi
WHERE bi.BookingID=#BookingID
FOR XML PATH('')
),1,1,'') AS BookingInfoCSV;
GO
--Hint the trick with XQuery data() function will break, if your values contain blanks!
--The following SELECT calls all rows from Booking-table and gets the fitting details
SELECT b.BookingID
,b.SomeBookingData
,A.BookingInfoCSV
FROM Booking AS b
OUTER APPLY dbo.CreateBookingInfoCSV(b.BookingID) AS A;
GO
--Clean up (carefull with real data!)
USE master;
GO
DROP DATABASE testDB;
--The result
BookingID SomeBookingData BookingInfoCSV
1 Booking 1 AP,AP,XY,MN
2 Booking 2 AA,BB
Having trouble importing an xml date feild into sql server, the other feilds are fine. I have tried numerous way's but it always returns as null. Any help please
2014-02-18T12:15:21.357 is the issue
The XML is
<?xml version="1.0" encoding="utf-8"?>
<MISRoot xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/IGUK.Evolution.FieldToolLite.Lib.MIS">
<Claims>
<Claim>
<Amalgamation>
<ChannelSolutions />
<Name i:nil="true" />
</Amalgamation>
<Appointments>
<Appointment>
<Access />
<AccessGranted>true</AccessGranted>
<AppointmentId>320</AppointmentId>
<EnterTime>2014-02-18T12:15:21.357</EnterTime>
<LeaveTime i:nil="true" />
<Name i:nil="true" />
</Appointment>
</Appointments>
<CustomProducts />
<Id>1220</Id>
<Payments />
<Tasks />
</Claim>
</Claims>
</MISRoot>
THE SQL is
CREATE TABLE [dbo].[xmlImportAppointments](
[ClaimId] [int] not null,
[AppointmentAUTO] [int] not NULL,
[AppointmentId] [int] not NULL,
[EnterTime] [datetime] NULL,
[LeaveTime] [datetime] NULL,
[AccessGranted] [nchar](20) NULL,
) ON [PRIMARY]
GO
WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/IGUK.Evolution.FieldToolLite.Lib.MIS')
INSERT INTO xmlImportAppointments
(ClaimId, AppointmentAUTO, AppointmentId, EnterTime, LeaveTime, AccessGranted)
SELECT
ClaimId = xmldata.value('(Claim/Id)[1]', 'int'),
AppointmentAUTO = xmldata.value('(Claim/Appointments/Appointment/AppointmentId)[1]', 'int'),
AppointmentId = xmldata.value('(Claim/Appointments/Appointment/AppointmentId)[1]', 'int'),
EnterTime = xmldata.value('(Claims/Appointment/Appointments/EnterTime)[1]', 'datetime'),
LeaveTime = xmldata.value('(Claims/Appointment/Appointments/EnterTime)[1]', 'datetime'),
AccessGranted = xmldata.value('(Claim/Appointments/Appointment/AccessGranted)[1]', 'NCHAR(20)')
FROM
(SELECT CAST(x AS XML)
FROM OPENROWSET(BULK '\\XMLTest\increment.xml',
SINGLE_BLOB) AS T(x)) AS T(x)
CROSS APPLY
x.nodes('/MISRoot/Claims') AS X(xmldata);
SELECT * FROM xmlImportAppointments
You had a problem with your XQuery I had copied your File into a XML variable and was able to retrieve using the following query.
DECLARE #MyXMLVariable XML =
'<?xml version="1.0" encoding="utf-8"?>
<MISRoot xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/IGUK.Evolution.FieldToolLite.Lib.MIS">
<Claims>
<Claim>
<Amalgamation>
<ChannelSolutions />
<Name i:nil="true" />
</Amalgamation>
<Appointments>
<Appointment>
<Access />
<AccessGranted>true</AccessGranted>
<AppointmentId>320</AppointmentId>
<EnterTime>2014-02-18T12:15:21.357</EnterTime>
<LeaveTime i:nil="true" />
<Name i:nil="true" />
</Appointment>
</Appointments>
<CustomProducts />
<Id>1220</Id>
<Payments />
<Tasks />
</Claim>
</Claims>
</MISRoot>';
WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/IGUK.Evolution.FieldToolLite.Lib.MIS')
SELECT #MyXMLVariable.value('(MISRoot/Claims/Claim/Appointments/Appointment/EnterTime)[1]','DATETIME')
You did not specify the full correct path to EnterTime
'(MISRoot/Claims/Claim/Appointments/Appointment/EnterTime)[1]' --Correct
'(Claims/Appointment/Appointments/EnterTime)[1]' --Yours
Appointment is child of Appointments not the other way around and Claim is a child of Claims best way to do this is to look at Open tags and closed tags
I have the following sql stored procedure to bulk insert and parse an xml file and insert its data into several tables in a database.
The sql below works, however its inserting duplicate records into the #questions table and the #cards table.
Any help on this would be much appreciated. Thanks in advance.
Here is the XML file:
<?xml version="1.0" encoding="UTF-8"?>
<Users>
<User>
<UserInfo>
<Id>0001</Id>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
<Email>Doejk#net.com</Email>
</UserInfo>
<Questions>
<Question>
<Id>1</Id>
<AnswerId>1</AnswerId>
</Question>
<Question>
<Id>2</Id>
<AnswerId>3</AnswerId>
</Question>
</Questions>
<Cards>
<Card>
<Id>1234</Id>
<Type>Digital</Type>
<Status>Active</Status>
</Card>
<Card>
<Id>1334</Id>
<Type>Physical</Type>
<Status>Not Active</Status>
</Card>
</Cards>
</User>
<User>
<UserInfo>
<Id>0002</Id>
<FirstName>Mary</FirstName>
<LastName>Doe</LastName>
<Email>Doem#net.com</Email>
</UserInfo>
<Questions>
<Question>
<Id>3</Id>
<AnswerId>6</AnswerId>
</Question>
<Question>
<Id>4</Id>
<AnswerId>7</AnswerId>
</Question>
</Questions>
<Cards>
<Card>
<Id>3333</Id>
<Type>Digital</Type>
<Status>Active</Status>
</Card>
<Card>
<Id>4444</Id>
<Type>Physical</Type>
<Status>Active</Status>
</Card>
</Cards>
</User>
</Users>
Here is SQL code
/*************************************************************************
-- CREATE TEMP TABLES --
**************************************************************************/
CREATE TABLE #XMLDATA
(RecordId int IDENTITY(1,1) NOT NULL,
XmlData xml NOT NULL)
CREATE TABLE #USERS
(UserID int null,
firstname varchar(50) null,
lastname varchar(50) null,
email varchar(50) null)
CREATE TABLE #CARDS
(CardId int null,
userid int null,
card_type varchar(50) null,
card_status varchar(50) null)
CREATE TABLE #QUESTIONS
(UserID int null,
Question_ID int null,
Answer_ID int null)
/*************************************************************************
-- LOAD THE WHOLE XML AS SINGLE BLOB --
**************************************************************************/
INSERT INTO #XMLDATA(XmlData)
SELECT *
FROM OPENROWSET(
BULK 'c:\users.xml', SINGLE_BLOB)
/*************************************************************************
-- INSERT USERS --
**************************************************************************/
INSERT INTO #USERS
(userid, firstname, lastname, email)
SELECT href.value('(Id/text())[1]', 'integer'),
href.value('(FirstName/text())[1]', 'varchar(50)'),
href.value('(LastName/text())[1]', 'varchar(50)'),
href.value('(Email/text())[1]', 'varchar(30)'),
FROM #XMLDATA CROSS APPLY
XmlData.nodes('Users') AS userinfo(href)
/*************************************************************************
-- INSERT QUESTIONS INFORMATION --
**************************************************************************/
INSERT INTO #QUESTIONS
(UserId, Question_ID, Answer_ID)
SELECT sref.value('(Id/text())[1]', 'integer'),
qref.value('(Id/text())[1]', 'integer'),
qref.value('(AnswerId/text())[1]', 'integer')
FROM #XMLDATA CROSS APPLY
XmlData.nodes('Users/Questions/Question') AS Ques(qref) CROSS APPLY
XmlData.nodes('Users') AS userinf(sref)
/*************************************************************************
-- INSERT CARD INFORMATION --
**************************************************************************/
INSERT INTO #CARDS
(userid, card_id, card_type,card_status)
SELECT sref.value('(Id/text())[1]', 'integer'),
cref.value('(Id/text())[1]', 'integer'),
cref.value('(Type/text())[1]', 'varchar(50)'),
cref.value('(Status/text())[1]', 'varchar(50)')
FROM #XMLDATA CROSS APPLY
XmlData.nodes('/Users/Cards/Card') AS cardlist(cref) CROSS APPLY
XmlData.nodes('/Users') AS userinf(sref)
I'm not sure what your CROSS JOIN is about.
Here is some shredding.
Tip: Comment out the INSERT portion and just do the SELECT portion until you get those tweaked correctly.
DECLARE #data XML;
SET #data =
N'
<Users>
<User>
<UserInfo>
<Id>0001</Id>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
<Email>Doejk#net.com</Email>
</UserInfo>
<Questions>
<Question>
<Id>1</Id>
<AnswerId>1</AnswerId>
</Question>
<Question>
<Id>2</Id>
<AnswerId>3</AnswerId>
</Question>
</Questions>
<Cards>
<Card>
<Id>1234</Id>
<Type>Digital</Type>
<Status>Active</Status>
</Card>
<Card>
<Id>1334</Id>
<Type>Physical</Type>
<Status>Not Active</Status>
</Card>
</Cards>
</User>
<User>
<UserInfo>
<Id>0002</Id>
<FirstName>Mary</FirstName>
<LastName>Doe</LastName>
<Email>Doem#net.com</Email>
</UserInfo>
<Questions>
<Question>
<Id>3</Id>
<AnswerId>6</AnswerId>
</Question>
<Question>
<Id>4</Id>
<AnswerId>7</AnswerId>
</Question>
</Questions>
<Cards>
<Card>
<Id>3333</Id>
<Type>Digital</Type>
<Status>Active</Status>
</Card>
<Card>
<Id>4444</Id>
<Type>Physical</Type>
<Status>Active</Status>
</Card>
</Cards>
</User>
</Users>';
SELECT T.myEntity.value('(Id)[1]', 'VARCHAR(20)')
, T.myEntity.value('(FirstName)[1]', 'VARCHAR(20)')
, T.myEntity.value('(LastName)[1]', 'VARCHAR(20)')
, T.myEntity.value('(Email)[1]', 'VARCHAR(20)')
FROM #data.nodes('Users/User/UserInfo') AS T(myEntity);
SELECT
T.myEntity.value('(../../UserInfo/Id)[1]', 'VARCHAR(20)')
, T.myEntity.value('(Id)[1]', 'INT')
, T.myEntity.value('(AnswerId)[1]', 'INT')
FROM #data.nodes('Users/User/Questions/Question') AS T(myEntity);
SELECT
T.myEntity.value('(../../UserInfo/Id)[1]', 'VARCHAR(20)')
, T.myEntity.value('(Id)[1]', 'INT')
, T.myEntity.value('(Type)[1]', 'VARCHAR(20)')
, T.myEntity.value('(Status)[1]', 'VARCHAR(20)')
FROM #data.nodes('Users/User/Cards/Card') AS T(myEntity);
CREATE TABLE #XMLDATA
(RecordId int IDENTITY(1,1) NOT NULL,
XmlData xml NOT NULL)
CREATE TABLE #USERS
(UserID int null,
firstname varchar(50) null,
lastname varchar(50) null,
email varchar(50) null)