How to iterate through a given xml input and insert the data in a TempTable in SQL Server? - sql-server

Hi I have the following xml input in a SP.
DECLARE #XmlVariable XML = '<portal><patientid>67518</patientid>
<forms>
<form id="31" type="C"/>
<form id="44" type="D"/>
</forms>
</portal>'
I have the following inmemory table:
DECLARE #TColumns table (
FormId int,
FormType varchar(1),
PatientId int
)
Now, my intention is to:
1.Iterate the xml and insert the values into the #TColumns table.
2.Read the #TColumns table row by row and based on the 3 column values update some existing table;something like
update myexistingtable set status=4 where Formid=31 && Formtype='C' and PatientId=67518.
For item number 1 above, this is what I have done till now, but there is some syntax error:
INSERT INTO #TColumns(FormId,FormType,PatientId)
SELECT
XTbl.Cats.value('.', 'int'),
XTbl.Cats.value('.', 'varchar(1)'),
XTbl.Cats.value('.', 'int')
FROM
#XmlVariable.nodes('/portal/forms/form/#id') AS XTbl(Cats),
#XmlVariable.nodes('/portal/forms/form/#type') AS XTbl(Cats),
#XmlVariable.nodes('/portal/forms/form/#patientid') AS XTbl(Cats)
The error I am getting is:The correlation name 'XTbl' is specified multiple times in a FROM clause.
Need help on this and also on the item number 2 above.
Thanks in advance.

Maybe you want something like this
SELECT
Tbl1.Form.value('#id', 'int'),
Tbl1.Form.value('#type', 'varchar(1)'),
Tbl2.Portal.value('patientid', 'int')
FROM
#XmlVariable.nodes('//form') Tbl1(Form),
#XmlVariable.nodes('//portal') Tbl2(Portal)

This is what helped.Yes its is based on Hogan's last suggestion.Thank you!
INSERT INTO #TColumns(FormId,FormType,PatientId)
SELECT
Tbl1.Form.value('#id', 'int'),
Tbl1.Form.value('#type', 'varchar(1)'),
Tbl2.Portal.value('.', 'int')
FROM
#XmlVariable.nodes('//form') Tbl1(Form),
#XmlVariable.nodes('//patientid') Tbl2(Portal)

Related

Finding a string in XML column using sql server

I have a table with a xml column.
I require to search for sub string in that xml column for all its node and value. Search should be case insensitive
Structure of XML in each row is different
I used below query to do that,
select * from TableName Where Cast(xmlcolumn as varchar(max) ) like '%searchString%'
this works for short length xml rows, if row length goes huge it cant handle the situation. Only partial of the data was searched.
Suggest me some other ways to achieve.
If this is one time task then I would use exist XML method thus:
DECLARE #Table1 TABLE (
ID INT IDENTITY PRIMARY KEY,
CommentAsXML XML
)
INSERT #Table1 (CommentAsXML)
VALUES (N'<root><item /><item type="Reg">0001</item><item type="Inv">B007</item><item type="Cus">A0001</item><item type="Br">F0001</item></root>')
INSERT #Table1 (CommentAsXML)
VALUES (N'<root><item /><item type="Reg">0005</item><parent><child>B007</child></parent><item type="Br">F0005</item></root>')
INSERT #Table1 (CommentAsXML)
VALUES (N'<root><item /><item type="Reg">0005</item></root>')
-- Following query is searching for B007 within InnerText of all XML elements:
SELECT *
FROM #Table1 t
WHERE t.CommentAsXML.exist('//*[lower-case(text()[1]) eq "b007"]') = 1
Results:
ID CommentAsXML
-- ------------------------------------------------------------------------------------------------------------------------------
1 <root><item type="Reg">0001</item><item type="Inv">B007</item><item type="Cus">A0001</item><item type="Br">F0001</item></root>
2 <root><item type="Reg">0005</item><parent><child>B007</child></parent><item type="Br">F0005</item></root>
Also, if you want to search for some text in XML atrributes' values then following XQuery could be used:
SELECT *
FROM #Table1 t
WHERE t.CommentAsXML.exist('//#*[lower-case(.) eq "reg"]') = 1
Note: in both cases, string constants (ex. "reg") should be with lower cases.

T-SQL Check if list has values, select and Insert into Table

I'm quite new to T-SQL and currently struggling with an insert statement in my stored procedure: I use as a parameter in the stored procedure a list of ids of type INT.
If the list is NOT empty, I want to store the ids into the table Delivery.
To pass the list of ids, i use a table type:
CREATE TYPE tIdList AS TABLE
(
ID INT NULL
);
GO
Maybe you know a better way to pass a list of ids into a stored procedure?
However, my procedure looks as follows:
-- parameter
#DeliveryModelIds tIdList READONLY
...
DECLARE #StoreId INT = 1;
-- Delivery
IF EXISTS (SELECT * FROM #DeliveryModelIds)
INSERT [MyDB].[Delivery] ([DeliveryModelId], [StoreId])
OUTPUT inserted.DeliveryId
SELECT ID FROM #DeliveryModelIds;
If the list has values, I want to store the values into the DB as well as the StoreId which is always 1.
If I insert the DeliveryIds 3,7,5 The result in table Delivery should look like this:
DeliveryId | StoreId | DeliveryModelId
1...............| 1...........| 3
2...............| 1...........| 7
3...............| 1...........| 5
Do you have an idea on how to solve this issue?
THANKS !
You can add #StoreId to your select for your insert.
...
IF EXISTS (SELECT * FROM #DeliveryModelIds)
INSERT [MyDB].[Delivery] ([DeliveryModelId], [StoreId])
OUTPUT inserted.DeliveryId
SELECT ID, #StoreId FROM #DeliveryModelIds;
Additionally, if you only want to insert DeliveryModelId that do not currently exist in the target table, you can use not exists() in the where clause like so:
...
IF EXISTS (SELECT * FROM #DeliveryModelIds)
INSERT [MyDB].[Delivery] ([DeliveryModelId], [StoreId])
OUTPUT inserted.DeliveryId
SELECT dmi.ID, #StoreId
FROM #DeliveryModelIds dmi
where not exists (
select 1
from MyDb.Delivery i
where i.StoreId = #StoreId
and i.DeliveryModeId = dmi.ID
);
You need to modify the INSERT statement to:
INSERT [MyDB].[Delivery] ([DeliveryModelId], [StoreId])
OUTPUT inserted.DeliveryId
SELECT ID, 1 FROM #DeliveryModelIds;
So you are also selecting a literal, 1, along with ID field.

Suggestions: xQuery where filter predicate delivers unexpected results

I have the following xml which is being parsed via a MSSQL database using OPENXML with an xquery filter to grab the right rows. Unfortunately, it doesn't seem to grab the appropriate rows, which has me scratching my head.
Using the following XML, I only want to insert the single email address where the Method="Insert", and ignore the remaining two addresses where Method is not present or has another value (which were previously inserted).
<Entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ActiveEntityID="0">
<Entity_Businesses>
<Entity_Business EntityTypeID="5" EntityRoleTypeID="9" Method="Update" Name="test business 76" EIN="" EmployeeCount="75" TotalAssets="750000.00">
<Entity_Emails>
<Entity_Email ID="85" EmailAddress="jones#company.com" />
<Entity_Email ID="0" EmailAddress="smith#company.com" Method="Insert"/>
</Entity_Emails>
<Entity_Contacts>
<Entity_Contact ID="162" EntityTypeID="4" EntityRoleTypeID="9" FName="Joe" MName="k" LName="Smith" SSN="444-44-444" JobTitleID="0" DOB="2007-02-27T00:00:00">
<Entity_Emails>
<Entity_Email ID="86" EmailAddress="individual#test.com"/>
</Entity_Emails>
</Entity_Contact>
</Entity_Contacts>
<Entity_Business>
</Entity_Businesses>
</Entities>
I am using this sql statement:
INSERT into Entity_Email(bsCol, EmailAddress, xmlID, xmlPID)
SELECT DENSE_RANK() OVER( ORDER BY y.parentid ) AS elementid, z.EmailAddress, y.parentid, z.ID
FROM OPENXML( #hDoc, '//Entity_Emails', 1 )
WITH (parentid int '#mp:parentid', id int '#mp:id' ) y
INNER JOIN OPENXML(#hDoc, N'//Entity_Emails/Entity_Email',1) WITH (EmailAddress nvarchar(100), xmlID int '#mp:id', parentid int '#mp:parentid') as z
ON y.id = z.parentid
WHERE #pRI.value('(//Entity_Emails/Entity_Email/#Method)[1]','nvarchar(50)') = 'Insert';
As-is, all three email addresses will be inserted, even though the first and last email node do not have a 'Method" attribute. However if I add 'Method = "DontAdd"' to the other two email addresses, nothing gets inserted.
I have also tried using the predicate:
WHERE #pRI.exist('//Entity_Emails/Entity_Email[#Method="Insert"]') =1;
The result is similar - it inserts all rows, and seems to ignore the fact that two of the Email_Address elements do not have an attribute Method="Insert", regardless of whether the Method attribute exists.
The goal is to filter the xml as it is shredded and only add the email address with the attribute Method="Insert". Right now what I believe I have is actually "If you find Method = 'Insert' in the dataset, insert all rows" vs. "if you find method = 'insert', insert only those rows which have that attribute."
Thank you in advance.
Please note the following answer for those that might be helped in the future. After retrieving the column 'Method' in the z aliased query, I was able to use standard t-sql to filter the results correctly and then insert the correct rows.
INSERT into Entity_Email(bsCol, EmailAddress, xmlID, xmlPID)
SELECT DENSE_RANK() OVER( ORDER BY y.parentid ) AS elementid, z.EmailAddress, z.xmlID, y.parentid
FROM OPENXML( #hDoc, '//Entity_Emails', 1 )
WITH (parentid int '#mp:parentid', id int '#mp:id' ) y
INNER JOIN OPENXML(#hDoc, N'//Entity_Emails/Entity_Email',1) WITH (EmailAddress nvarchar(100), xmlID int '#mp:id', parentid int '#mp:parentid', Method nvarchar(50) '#Method') as z
ON y.id = z.parentid
WHERE z.Method = 'Insert'

SQL Server : Verify a list of text from a table

Could you please help me with a query for the following requirements?
I have a list of Cities which I want to verify from a table. The query will have the Input List (below) in the where clause and gives me results like shown below in expected result.
Input list = ('City1','City2','City3','City4',.......'City100')
Expected result
1. City1 Exist
2. City2 Exist
3. City3 Not Exist
4. City4 Exist
5. City5 Not Exist
Thanks.
Try it like this: Just paste my code into an empty query window and execute. Adapt to your needs (most important is to replace the "#ExistingCities" with your actual table's name):
First I declare a "table" with some already existing cities (3 and 5 are missing). The input string misses City4.
DECLARE #ExistingCities TABLE(CityName VARCHAR(100));
INSERT INTO #ExistingCities VALUES('City1'),('City2'),('City4'),('City6');
DECLARE #Input VARCHAR(MAX)='City1,City2,City3,City5,City6';
WITH SplittedInput AS
(
SELECT CAST('<x>' + REPLACE(#input,',','</x><x>') + '</x>' AS XML) AS CitiesAsXml
)
SELECT oneCity.value('.','varchar(max)') AS InputName
,CASE WHEN exC.CityName IS NOT NULL THEN 'Exist' ELSE 'Not Exist' END AS ResultCode
FROM SplittedInput
CROSS APPLY CitiesAsXml.nodes('/x') AS InputCity(oneCity)
LEFT JOIN #ExistingCities AS exC ON exC.CityName=oneCity.value('.','varchar(max)')
Please check following SQL script,
For demo I created a sql CityList table with sample data
create table cityList (id int identity(1,1), city nvarchar(50),)
insert into cityList(city) values ('City1'),('City3')
declare #input nvarchar(max) = 'City1,City2,City3,City4,City100'
select
s.val,
case when c.city is not null then 'Exist' else 'Not Exist' end
from dbo.split(#input,',') s
left join cityList c on c.city = s.val
Please note that you need the SQL string Split function codes for dbo.split() function
I hope it helps

Update data in the table using XML string

I have table which contains columns like
SiteID (identity_Col)
SiteName
POrderID
Location
Address
Cluster
VenderName
I want to use xml string to insert/update/delete data in this table. Apart from this column, XML string contains one more column viz. RowInfo. This column will have values like "Unchanged","Update","New","Delete". Based on this values the rows in the table should be inserted,updated,deleted.
My XML string is as below:
<NewDataSet>
<DataTable>
<SiteID>2</SiteID>
<SiteName>NIZAMPURA</SiteName>
<POrderID>7</POrderID>
<Location>NIZAMPURA</Location>
<SiteAddress>Vadodara</SiteAddress>
<Cluster>002</Cluster>
<SubVendorName>Test Vender-1</SubVendorName>
<RowInfo>UNCHANGED</RowInfo>
</DataTable>
<DataTable>
<SiteID>16</SiteID>
<SiteName>Site-1</SiteName>
<POrderID>7</POrderID>
<Location>Alkapuri</Location>
<SiteAddress>test</SiteAddress>
<Cluster>Test Cluster</Cluster>
<SubVendorName>Test Vender12</SubVendorName>
<RowInfo>UNCHANGED</RowInfo>
</DataTable>
<DataTable>
<SiteID>17</SiteID>
<SiteName>Site-3</SiteName>
<POrderID>7</POrderID>
<Location>Alkapuri123</Location>
<SiteAddress>test123</SiteAddress>
<Cluster>Test Cluster123</Cluster>
<SubVendorName>Test Vender123</SubVendorName>
<RowInfo>DELETE</RowInfo>
</DataTable>
</NewDataSet>'
This is the code that I have written to insert data in table if RowInfo = "NEW"
IF len(ISNULL(#xmlString, '')) > 0
BEGIN
DECLARE #docHandle1 int = 0;
EXEC sp_xml_preparedocument #docHandle1 OUTPUT, #xmlString
INSERT INTO [SiteTRS] (
[SiteName],
[POrderID],
[Location],
[SiteAddress],
[Cluster],
[SubVendorName])
SELECT SiteName,POrderID,Location,SiteAddress,Cluster,SubVendorName
FROM OPENXML (#docHandle1, '/NewDataSet/DataTable')
WITH (SiteName varchar(50) './SiteName',
POrderID varchar(50) './PorderID',
Location varchar(50) './Location',
SiteAddress varchar(max) './SiteAddress',
Cluster varchar(50) './Cluster',
SubVendorName varchar(50) './SubVendorName',
RowInfo varchar(30) './RowInfo')
WHERE RowInfo='NEW'
But I don't know how to use XML to update/delete records in the table. Please guide
I am novice in the XML so dont have any idea. Please forgive me if I am making something childish.
I would strongly recommend not to use the old, legacy OPENXML stuff anymore - with XML support in SQL Server, it's much easier to use the built-in XPath/XQuery methods.
In your case, I would use a CTE (Common Table Expression) to break up the XML into an "inline" table of rows and columns:
DECLARE #input XML = '<NewDataSet>
<DataTable>
<SiteID>2</SiteID>
<SiteName>NIZAMPURA</SiteName>
<POrderID>7</POrderID>
<Location>NIZAMPURA</Location>
<SiteAddress>Vadodara</SiteAddress>
<Cluster>002</Cluster>
<SubVendorName>Vender-1</SubVendorName>
<RowInfo>UPDATE</RowInfo>
</DataTable>
<DataTable>
<SiteName>Site-1</SiteName>
<POrderID>7</POrderID>
<Location>Alkapuri</Location>
<SiteAddress>test</SiteAddress>
<Cluster>Cluster-1</Cluster>
<SubVendorName>Test Vender</SubVendorName>
<RowInfo>NEW</RowInfo>
</DataTable>
</NewDataSet>'
;WITH XMLData AS
(
SELECT
NDS.DT.value('(SiteID)[1]', 'int') AS 'SiteID',
NDS.DT.value('(SiteName)[1]', 'varchar(50)') AS 'SiteName',
NDS.DT.value('(POrderID)[1]', 'int') AS 'POrderID',
NDS.DT.value('(Location)[1]', 'varchar(100)') AS 'Location',
NDS.DT.value('(SiteAddress)[1]', 'varchar(100)') AS 'SiteAddress',
NDS.DT.value('(Cluster)[1]', 'varchar(100)') AS 'Cluster',
NDS.DT.value('(SubVendorName)[1]', 'varchar(100)') AS 'SubVendorName',
NDS.DT.value('(RowInfo)[1]', 'varchar(20)') AS 'RowInfo'
FROM
#input.nodes('/NewDataSet/DataTable') AS NDS(DT)
)
SELECT *
FROM XMLDATA
This gives you rows and columns which you can work with.
SiteID SiteName POrderID Location SiteAddress Cluster SubVendorName RowInfo
2 NIZAMPURA 7 NIZAMPURA Vadodara 002 Vender-1 UPDATE
NULL Site-1 7 Alkapuri test Cluster-1 Test Vender NEW
Now if you're on SQL Server 2008 or newer, you could combine this with the MERGE command to do your INSERT/UPDATE in a single statement, basically.
If you're on 2005, you will need to either store this information into a temporary table / table variable inside your stored proc, or you need to do the select multiple times; the CTE allows only one single command to follow it.
Update: with this CTE, you can then combine it with a MERGE:
;WITH XmlData AS
(
SELECT
NDS.DT.value('(SiteID)[1]', 'int') AS 'SiteID',
NDS.DT.value('(SiteName)[1]', 'varchar(50)') AS 'SiteName',
NDS.DT.value('(POrderID)[1]', 'int') AS 'POrderID',
NDS.DT.value('(Location)[1]', 'varchar(100)') AS 'Location',
NDS.DT.value('(SiteAddress)[1]', 'varchar(100)') AS 'SiteAddress',
NDS.DT.value('(Cluster)[1]', 'varchar(100)') AS 'Cluster',
NDS.DT.value('(SubVendorName)[1]', 'varchar(100)') AS 'SubVendorName',
NDS.DT.value('(RowInfo)[1]', 'varchar(20)') AS 'RowInfo'
FROM
#input.nodes('/NewDataSet/DataTable') AS NDS(DT)
)
MERGE INTO dbo.SiteTRS t
USING XmlData x ON t.SiteID = x.SiteID
WHEN MATCHED AND x.RowInfo = 'UPDATE'
THEN
UPDATE SET
t.SiteName = x.SiteName,
t.POrderID = x.POrderID,
t.Location = x.Location,
t.SiteAddress = x.SiteAddress,
t.Cluster = x.Cluster,
t.SubVendorName = x.SubVendorName
WHEN MATCHED AND x.RowInfo = 'DELETE'
THEN DELETE
WHEN NOT MATCHED AND x.RowInfo = 'NEW'
THEN
INSERT(SiteID, SiteName, POrderID, Location, SiteAddress, Cluster, SubVendorName)
VALUES(x.SiteID, x.SiteName, x.POrderID, x.Location, x.SiteAddress, x.Cluster, x.SubVendorName)
;
See some more resources:
SQL SERVER – 2008 – Introduction to Merge Statement – One Statement for INSERT, UPDATE, DELETE
Using SQL Server 2008's MERGE statement

Resources