I have a table with the following columns:
id, xml, receivedDate, processed, processedDateTime, orderNumber
id, receivedDate, and processed all have default values.
The XML being stored in the xml column looks like this:
<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
<v1:BusinessUnitId>11</v1:BusinessUnitId>
<v1:OrderNumber>964806604</v1:OrderNumber>
<v1:OrderStatusCode>RDD</v1:OrderStatusCode>
<v1:StatusDateTime>2016-04-12T01:16:28</v1:StatusDateTime>
<v1:RDD_Date>2016-04-19T00:00:00</v1:RDD_Date>
</v1:OrderStatus>
When the XML is inserted to the table, I need to grab the value for OrderNumber and set it in the orderNumber column. I developed a trigger to try to accomplish this, but instead of setting the the value in only the newly inserted row, it sets the value for ALL the rows in the table.
My trigger looks like:
declare #ordernumber varchar(50)
select #ordernumber=(select xml.value('(//*:OrderNumber)[1]', 'varchar(max)') as orderNumber from inserted)
Update [dbo].[dell.des.OCIMessages]
set orderNumber=#ordernumber
END
I have another trigger on a different table which joins on the orderNumber value between these two tables and I use this other trigger after an insert/update to find the row with the matching orderNumber and processed=0 and then set processed=1 and also set the processedDateTime field.
Unfortunately, since my first trigger is updating all the rows to use the same order number value, my second trigger inadvertently always updates all the rows because the ordernumber values are the same for every row in my table.
Can someone please shed some light on what is wrong with my trigger so that it only updates just the inserted row?
BTW, I'm only ever inserting a single row at a time, so I don't have to worry about my trigger have to deal with multiple records.
Thanks!
You could use a where condition on your update:
declare #ordernumber varchar(50), #id int
select #ordernumber= xml.value('(//*:OrderNumber)[1]', 'varchar(max)') as orderNumber, #id = id from inserted
Update [dbo].[dell.des.OCIMessages]
set orderNumber=#ordernumber
where id = #id
END
Or you could just use the set-based option:
Update oci Set oci.orderNumber = inserted.xml.value('(//*:OrderNumber)[1]', 'varchar(max)')
from [dbo].[dell.des.OCIMessages] oci join inserted on oci.id = inserted.id
I place a second answer, because this is a completely differing approach:
Create a normal table
CREATE TABLE dbo.Test(id INT, xml XML, OrderNumber VARCHAR(100));
Create an "INSTEAD OF" Trigger
CREATE TRIGGER dbo.InsertToTest ON dbo.Test
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO dbo.Test(id,xml,OrderNumber)
SELECT id,xml,xml.value('(//*:OrderNumber)[1]', 'varchar(max)')
FROM inserted
END
GO
And this is how it works
declare #xml1 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
<v1:OrderNumber>11111</v1:OrderNumber>
</v1:OrderStatus>';
declare #xml2 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
<v1:OrderNumber>22222</v1:OrderNumber>
</v1:OrderStatus>';
declare #xml3 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
<v1:OrderNumber>33333</v1:OrderNumber>
</v1:OrderStatus>';
insert into dbo.Test VALUES(1,#xml1,'SomeValue'),(2,#xml2,'AnotherValue');
select * from dbo.Test;
insert into dbo.Test select 3,#xml3,null;
select * from dbo.Test;
What about this?
You need a unser defined function to retrieve your value
CREATE FUNCTION dbo.GetOrderNumber(#xml xml)
RETURNS VARCHAR(MAX)
AS
BEGIN
RETURN #xml.value('(//*:OrderNumber)[1]', 'varchar(max)');
END
Define your table with a computed column
CREATE TABLE dbo.Test(id INT, xml XML, OrderNumber AS dbo.GetOrderNumber(xml));
And this is how it works
declare #xml1 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
<v1:OrderNumber>11111</v1:OrderNumber>
</v1:OrderStatus>';
declare #xml2 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
<v1:OrderNumber>22222</v1:OrderNumber>
</v1:OrderStatus>';
declare #xml3 xml=
'<v1:OrderStatus xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v1="http://xmlns.dell.com/Services/DMT/OrderStatus/V1">
<v1:OrderNumber>33333</v1:OrderNumber>
</v1:OrderStatus>';
insert into dbo.Test VALUES(1,#xml1),(2,#xml2);
select * from dbo.Test;
insert into dbo.Test select 3,#xml3;
select * from dbo.Test;
Related
Is very simple to explain what i want to do but I think is a little bit difficult to implment, well... the think is that Im doing a stored procedure that recives an XML param, then Im doing an insert select with the records in the XML, somethin like this:
insert into #pedXMl
SELECT distinct
LTRIM(RTRIM(nref.value('#COL_0','VARCHAR(50)'))) NumPedido,
LTRIM(RTRIM(nref.value('#COL_1','VARCHAR(50)'))) CodAlmacen
FROM #PI_ParamXML.nodes('/CARGAPEDIDO/REGISTRO') as R(nref)
WHERE LTRIM(nref.value('#COL_0','VARCHAR(50)')) <> ''
The thing is that in my xml comming like parameter will be records with error, what I want is to keep inserting ALTHOUGH a record throws error.
Sorry my bad English Im traying the best, I hope you were underdstood and can help with this! thanks!
This depends on What is an error?
This might be
invalid XML (only if you pass in the XML as string and cast it internally)
invalid internal structue (nodes/attributes missing, bad nestings...)
wrong values in casts (should be INT, but isn't)
broken business rules (e.g. value cannot be 1 if other value is 0)
Anyway: One Statement is one step. It will work in total or it will fail.
One way to achieve what you want is a row-by-row approach (CURSOR), the second was a set based approach with a staging table:
As an example I assume it's the wrong value, but the general approach should be quite the same with other problems:
The second Data-node has an invalid valueInt
DECLARE #passedIn XML=
'<root>
<data rowID="1">
<valueInt>111</valueInt>
<valueString>hello1</valueString>
</data>
<data rowID="2">
<valueInt>222xyz</valueInt>
<valueString>hello2</valueString>
</data>
<data rowID="3">
<valueInt>333</valueInt>
<valueString>hello3</valueString>
</data>
</root>';
This is the CURSOR approach
DECLARE #target TABLE(RowID INT,valueInt INT,valueString NVARCHAR(MAX));
DECLARE #errors TABLE(rowID INT,DataNode XML);
DECLARE #rowID INT;
DECLARE #vInt NVARCHAR(MAX); --type is NVARCHAR!!!
DECLARE #vString NVARCHAR(MAX);
DECLARE #DataNode XML;
DECLARE cur CURSOR FOR
(
SELECT data.value('#rowID','int') AS RowID
,data.value('valueInt[1]','nvarchar(max)') AS ValueInt --type is NVARCHAR!!!
,data.value('valueString[1]','nvarchar(max)') AS ValueString
,data.query('.') AS DataNode
FROM #passedIn.nodes('/root/data') AS A(data)
);
OPEN cur;
FETCH NEXT FROM cur INTO #rowID,#vInt,#vString,#DataNode;
WHILE ##FETCH_STATUS=0
BEGIN
BEGIN TRY
INSERT INTO #target VALUES(#rowID,#vInt,#vString);
END TRY
BEGIN CATCH
INSERT INTO #errors VALUES(#rowID,#DataNode);
END CATCH
FETCH NEXT FROM cur INTO #rowID,#vInt,#vString,#DataNode;
END
CLOSE cur;
DEALLOCATE cur;
--This is the result
SELECT * FROM #target;
SELECT * FROM #errors;
Clean up and re-start with the set-based approach via staging table
DELETE FROM #target;
DELETE FROM #errors;
SELECT data.value('#rowID','int') AS RowID
,data.value('valueInt[1]','nvarchar(max)') AS ValueInt --type is NVARCHAR!!!
,data.value('valueString[1]','nvarchar(max)') AS ValueString
,data.query('.') AS DataNode
INTO #stagingTable
FROM #passedIn.nodes('/root/data') AS A(data);
INSERT INTO #target
SELECT RowID,ValueInt,ValueString
FROM #stagingTable
WHERE ISNUMERIC(ValueInt)=1;
INSERT INTO #errors
SELECT RowID,DataNode
FROM #stagingTable
WHERE ISNUMERIC(ValueInt)=0;
SELECT * FROM #target;
SELECT * FROM #errors;
GO
DROP TABLE #stagingTable;
I have a xml column that look like
SET #XMLData = '<ArrayOfEntityNested xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.Bijak">
<EntityNested>
<Id xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto">1</Id>
<Date xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak">0001-01-01T00:00:00</Date>
<Description xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak">deesc</Description>
<Number xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak" i:nil="true" />
</EntityNested>
</ArrayOfEntityNested>'
I need insert data from the XML into a temp table.
here
For this I use from following code. But it's not working, and it's not inserting any data into temp table.
--Variables Decleration
DECLARE #XMLData VARCHAR(MAX)
DECLARE #idoc INT
-- Creating Temporary Table
CREATE TABLE #TEMP_TABLE
(
REC_ID INT IDENTITY(1,1),
[Id] INT,
[Date] VARCHAR(50),
[Number] VARCHAR(50),
);
--Case 1
SET #XMLData = '<ArrayOfEntityNested xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.Bijak">
<EntityNested>
<Id xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto">1</Id>
<Date xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak">0001-01-01T00:00:00</Date>
<Number xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak" i:nil="true" />
</EntityNested>
</ArrayOfEntityNested>
'
--Reading Data from XML and inserting into Temp Table
EXECUTE sp_xml_preparedocument #idoc OUTPUT, #XMLData
INSERT INTO #TEMP_TABLE
SELECT *
FROM OpenXML(#idoc,'/ArrayOfEntityNested/EntityNested', 1)
WITH #TEMP_TABLE
EXECUTE sp_xml_removedocument #idoc
--Displaying data from Temp Table
SELECT * FROM #TEMP_TABLE
DROP TABLE #TEMP_TABLE;
But that doesn't work, if xml format correct might look like :
SET #XMLData = '<ArrayOfEntityNested>
<EntityNested>
<Id>1</Id>
<Date>0001-01-01T00:00:00</Date>
<Description>deesc</Description>
<EmployeeId>2</EmployeeId>
<IsDeleted>false</IsDeleted>
<LoadingPermitTruckId>7541</LoadingPermitTruckId>
</EntityNested>
</ArrayOfEntityNested>'
then it works.
Please help me.
First of all - please use appropriate data types! If your source data is XML - why aren't you using the XML datatype?
Also, if you have a Date in your table - why isn't that a DATE or DATETIME type?? And why is the Number a VARCHAR(50) ??
Makes no sense......
Then: you're not looking at the XML namespaces that are present in the XML document - but you must!
At lastly - I would recommend using the native XQuery support instead of the legacy, deprecated sp_xml_preparedocument / OpenXML approach....
Seems much easier, much clearer to me...
Use this:
-- variable declaration
DECLARE #XMLData XML
-- creating temporary table
CREATE TABLE #TEMP_TABLE
(
REC_ID INT IDENTITY(1,1),
[Id] INT,
[Date] DATETIME2(3),
[Number] INT
);
and then use proper XQuery statements, including the XML namespaces to handle the data:
SET #XMLData = '<ArrayOfEntityNested xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.Bijak">
<EntityNested>
<Id xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto">1</Id>
<Date xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak">0001-01-01T00:00:00</Date>
<Number xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak" i:nil="true" />
</EntityNested>
<EntityNested>
<Id xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto">42</Id>
<Date xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak">2013-12-22T14:45:00</Date>
<Number xmlns="http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak">373</Number>
</EntityNested>
</ArrayOfEntityNested>'
;WITH XMLNAMESPACES ('http://schemas.datacontract.org/2004/07/Gbms.Dto.Bijak' AS ns1,
'http://schemas.datacontract.org/2004/07/Gbms.Dto' AS ns2,
'http://schemas.datacontract.org/2004/07/Gbms.Dto.VirginBijak' AS ns3)
INSERT INTO #TEMP_TABLE(ID, Date, Number)
SELECT
xc.value('(ns2:Id)[1]', 'int'),
xc.value('(ns3:Date)[1]', 'DateTime2'),
xc.value('(ns3:Number)[1]', 'int')
FROM
#XmlData.nodes('/ns1:ArrayOfEntityNested/ns1:EntityNested') AS xt(xc)
DECLARE #idoc int
DECLARE #doc varchar(1000)
SET #doc ='
<OutLookContact>
<Contact FirstName="Asif" LastName="Ghafoor" EmailAddress1="asifghafoor#my.web.pk" />
<Contact FirstName="Rameez" LastName="Ali" EmailAddress1="rameezali#my.web.pk" />
</OutLookContact>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT statement that uses the OPENXML rowset provider.
DECLARE #Temp TABLE(FirstName VARCHAR(250),LastName VARCHAR(250),Email1 VARCHAR(250))
INSERT INTO #Temp(FirstName,LastName,Email1)
SELECT *
FROM OPENXML (#idoc, '/OutLookContact/Contact',1)
WITH (FirstName varchar(50),LastName varchar(50),EmailAddress1 varchar(50))
select FirstName,LastName,Email1 from #Temp
it will be a lot easier if you try to use a tool called pentaho. http://en.wikipedia.org/wiki/Pentaho
it is an open source tool which is used for data integration.you can create a database connection from mysql or oracle to it and do the transformation.it is easy to use.
I have a requirement where I have to insert multiple rows into different tables.
First I want to check whether the role exists in one table or not, then I have to insert the values into the role table then inserting into the related table.
Here my concern is I have to get the roleid if it is inserting and with that roleid only I will insert the values.
This all should be done in a single click and single stored procedure.
Waiting for reply.
Thanks in advance .
Like #dasblinkenlight stated you can use the merge function to do an insert or update, so no need to cover that again.
If I understand your question correctly you want to insert multiple rows into different tables with a single stored procedure (something like persisting an entire object graph in one call). Extending on the example provided by Microsoft you can use the following sample code.
DECLARE #idoc int
DECLARE #doc varchar(1000)
SET #doc ='
<CustomersAndOrders>
<Customer CustomerID="VINET" ContactName="Paul Henriot">
<Order CustomerID="VINET" EmployeeID="5" OrderDate="1996-07-04T00:00:00" />
</Customer>
<Customer CustomerID="LILAS" ContactName="Carlos Gonzlez">
<Order CustomerID="LILAS" EmployeeID="3" OrderDate="1996-08-16T00:00:00" />
</Customer>
</CustomersAndOrders>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT statement that uses the OPENXML rowset provider.
INSERT INTO [YOUR_TABLE_NAME] ([Column1], [Column2])
SELECT [CustomerID], [ContactName]
FROM OPENXML (#idoc, '/CustomersAndOrders/Customer',1)
WITH ([CustomerID] varchar(10), [ContactName] varchar(20))
INSERT INTO [YOUR_OTHER_TABLE_NAME] ([Column1], [Column2])
SELECT [EmployeeID], [OrderDate]
FROM OPENXML (#idoc, '/CustomersAndOrders/Customer/Order',1)
WITH ([EmployeeID] varchar(10), [OrderDate] varchar(20))
I have a column of ntext data type and NOT XML. It stores all xml data. I need to update xml node in the records. It throws an error saying "Incorrect use of the xml data type method 'modify'. A non-mutator method is expected in this context."
begin transaction
declare #Cps_Id int;
set #Cps_Id = 236;
declare #Cps_Message nvarchar(1024);
set #Cps_Message = 'updating cpsia message with smoking';
update table_name
set column_name = CONVERT(xml,column_name).modify('replace value of (/root/ProductInformation/CPSIA/CpsiaDetails/Item[CpsiaId=sql:variable("#Cps_Id")]/CpsiaMessage/text())[1] with sql:variable("#Cps_Message")')
WHERE Convert(xml,column_name).exist('/root/ProductInformation/CPSIA/CpsiaDetails/Item[CpsiaId=sql:variable("#Cps_Id")]')=1
rollback
Sample XML:
<root>
<ProductInformation>
<Name> Truck with Battery Charger</Name>
<Description>Fr.</Description>
<CPSIA>
<CpsiaDetails>
<Item>
<CpsiaId>456</CpsiaId>
<CpsiaMessage>waring</CpsiaMessage>
</Item>
<Item>
<CpsiaId>236</CpsiaId>
<CpsiaMessage>to health</CpsiaMessage>
</Item>
</CpsiaDetails>
</CPSIA>
</ProductInformation>
</root>
You need to use the modify method on the XML data type.
begin transaction
declare #Cps_Id int;
set #Cps_Id = 236;
declare #Cps_Message nvarchar(1024);
set #Cps_Message = 'updating cpsia message with smoking';
select id, CONVERT(xml,[text]) txt into #tmp from SO5954359
select * from #tmp
update #tmp
set txt.modify('replace value of (/root/ProductInformation/CPSIA/CpsiaDetails/Item[CpsiaId=sql:variable("#Cps_Id")]/CpsiaMessage/text())[1] with sql:variable("#Cps_Message")')
select * from #tmp
drop table #tmp
rollback
You could then update the original table by joining the updated temporary table to the original table on the key.
I have an XML file and I open that in SQL Server using OPENXML and then read the values in the XML file and insert them into the table. Assume that the XML structure is like this
<Student>
<name>XYZ</name>
<id>123</id>
<fathersname>XYS</fathersname>
<fathersid>3489</fathersid>
</Student>".
Now I need to add this as two different rows and the DB should look like this
Name ID XYZ 123XYS 3489 How can i read from thisXML and insert as two different rows using a single OPENXML statement?
CREATE TABLE dbo.Person(ID int, [Name] varchar(50))
DECLARE #docHandle int
DECLARE #xmlDocument XML
SET #xmlDocument = N'<ROOT>
<Student>
<name>XYZ</name>
<id>123</id>
<fathersname>XYS</fathersname>
<fathersid>3489</fathersid>
</Student>
<Student>
<name>ABC</name>
<id>456</id>
<fathersname>DEF</fathersname>
<fathersid>7859</fathersid>
</Student>
</ROOT>'
EXEC sp_xml_preparedocument #docHandle OUTPUT, #xmlDocument
-- student's data first
INSERT INTO dbo.Person
SELECT *
FROM OPENXML(#docHandle, N'/ROOT/Student',2)
WITH (id int, name varchar(50))
-- now insert father's data
INSERT INTO dbo.Person
SELECT *
FROM OPENXML(#docHandle, N'/ROOT/Student',2)
WITH (fathersid int, fathersname varchar(50))
SELECT * FROM dbo.Person
EXEC sp_xml_removedocument #docHandle
DROP TABLE dbo.Person
To open from a file:
declare #xmlDocument XML
SET #xmlDocument=(SELECT * FROM OPENROWSET(
BULK 'c:\Temp\Student.xml',
SINGLE_BLOB) AS x)
UPDATE:
Sorry, did not see that you are trying to split <father> into a different row, I have simply added one more INSERT for that. If you need more control over the loading process, you can always consider setting up an ETL job in SSIS.
UPDATE 2
Well here is a creative way with one insert only, but two selects -- not sure about the performance at all. Again, we are splitting one record into two rows.
INSERT INTO dbo.Person
SELECT
x.e.value('id[1]', 'int') AS "id"
,x.e.value('name[1]', 'varchar(10)') AS "Name"
FROM #xmlDocument.nodes('/ROOT/Student') AS x(e)
UNION
SELECT
x.e.value('fathersid[1]', 'int') AS "id"
,x.e.value('fathersname[1]', 'varchar(10)') AS "Name"
FROM #xmlDocument.nodes('/ROOT/Student') AS x(e);