Stored procedure to read xml file and Scope_identity error - sql-server

I am reading an xml file in a stored procedure to insert data into an existing table. I have read the xml in a small test to see if I can read it like I think should be able to and it does. I now want to create a stored procedure that inserts data from xml in a 'Question' table and also an 'Answer' table. The thing is I need the primary key from the question table that was just created to insert into the Answer table or else it causes a problem because of the FK.
I have this:
insert into Questions(Question_Text, Questionaire_ID, QuestionType, Filter)
select
X.xmlData.query('Question').value('.', 'varchar(100)') Question_Text,--Question Text
X.xmlData.query('QuestionaireID').value('.', 'varchar(100)') Questionaire_ID,-- Questionaire ID
X.xmlData.query('Type').value('.', 'varchar(100)') WuestioneType,--Question TYPE
X.xmlData.query('Filter').value('.', 'varchar(100)') Filter--Filter
from
(select
cast(x as XML)
from openrowset(bulk 'C:\sqlXML.xml', single_blob) as T(x)
) as T(x)
cross apply
x.nodes('data/New') as X(xmlData);
select #newQuestionId = SCOPE_IDENTITY() <--I need this to know the current
questionID that relate to the following possible answers
insert into Possible_Answers(Question_ID, Possible_Answer_Text,
Explanation_Required, Review_Required, Question_Type)
select #newQuestionId ,
X.xmlData.query('AnswerChoice[1]').value('.', 'varchar(100)') Possible_Answer_Text,
X.xmlData.query('AnswerChoice[2]').value('.', 'varchar(100)') Possible_Answer_Text,
X.xmlData.query('AnswerChoice[3]').value('.', 'varchar(100)') Possible_Answer_Text,
X.xmlData.query('AnswerChoice[4]').value('.', 'varchar(100)') Possible_Answer_Text,
X.xmlData.query('ExplanationRequired').value('.', 'varchar(100)') Explanation_Required,
X.xmlData.query('ReviewRequired').value('.', 'varchar(100)') Review_Rewuired
from (
select cast(x as XML)
from openrowset(
bulk 'C:\sqlXML.xml',
single_blob) as T(x)
)
as T(x)
cross apply x.nodes('data/New') as X(xmlData);
Now this could be all wrong because in my test I just read the data and didn't insert it into a table.
And this is my XML:
<?xml version="1.0" encoding="UTF-8" ?>
<data>
<New>
<QuestionaireID>2</QuestionaireID>
<Type>1</Type>
<Question>Does this test work?</Question>
<Filter>31</Filter>
<AnswerChoice>true</AnswerChoice>
<AnswerChoice>false</AnswerChoice>
<ExplanationRequired></ExplanationRequired>
<ReviewRequired></ReviewRequired>
</New>
<New>
<QuestionaireID>3</QuestionaireID>
<Type>2</Type>
<Question>Does this test work really?</Question>
<Filter>127</Filter>
<AnswerChoice>answer A</AnswerChoice>
<AnswerChoice>Answer B</AnswerChoice>
<AnswerChoice>Answer C</AnswerChoice>
<AnswerChoice>Answer D</AnswerChoice>
<ExplanationRequired></ExplanationRequired>
<ReviewRequired></ReviewRequired>
</New>
</data>
As you can see there may be question with on 2 possible answers or 4 possible answers...
I get the errors:
An INSERT statement cannot contain a SELECT statement that assigns values to a variable.
A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
I know where the errors are coming from but I was wondering if there was a way to do this?

Related

T-SQL: Parse XML Data in from classes which inherit from a common base,

I'm trying to parse XML data in SQL Server. I have a XML column in a table, the XML stored in it can vary by type, but they all inherit from the same base type.
Row 1: has XML like so:
<Form>
<TaskType>1</TaskType>
--Other Properties ...
</Form>
Row 2: has XML like so:
<License>
<TaskType>2</TaskType>
--Other Properties ...
</License>
Normally I might parse XML with this T-SQL code snippet:
SELECT
xmlData.A.value('.', 'INT') AS Animal
FROM
#XMLToParse.nodes('License/TaskType') xmlData(A)
This doesn't work since in a view since I'm dependent on the name to find the node.
How can I always find the TaskType XML element in my XML content?
Please try the following solution.
XPath is using asterisk * as a wildcard.
http://www.tizag.com/xmlTutorial/xpathwildcard.php
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT #tbl (xmldata) VALUES
(N'<Form>
<TaskType>1</TaskType>
<TaskName>Clone</TaskName>
<!--Other XML elements-->
</Form>'),
(N'<License>
<TaskType>2</TaskType>
<TaskName>Copy</TaskName>
<!--Other XML elements-->
</License>');
-- DDL and sample data population, end
SELECT ID
, c.value('(TaskType/text())[1]', 'INT') AS TaskType
, c.value('(TaskName/text())[1]', 'VARCHAR(20)') AS TaskName
FROM #tbl
CROSS APPLY xmldata.nodes('/*') AS t(c);
Output
ID
TaskType
TaskName
1
1
Clone
2
2
Copy
Apparently you can just interate the nodes like so without being aware of their name:
SELECT xmlData.A.value('.', 'INT') AS Animal
FROM #XMLToParse.nodes('node()/TaskType') xmlData(A)

Modify XML in SQL field stored as Varbinary

My XML is stored as varbinary in a field.
SELECT cast (inboxXml as xml) FROM globalDB.Inbox WHERE inboxCId = '207435-N'
I would like to update one attribute (below). However the error is "Cannot call methods on varbinary(max)." I tried different ways to cast it, but I cannot find it.
thank you,
;WITH XMLNAMESPACES(DEFAULT 'http://www.w3.org/2001/XMLSchema')
UPDATE globalDB.Inbox
SET inboxXml.modify('replace value of (//ReceiveDeliveryHeader/DocumentID/ID/#accountingEntity[.="ABC"])[1] with "ZZZ"')
WHERE inboxCId = '207435-N'
The first question is: Why are you storing your XML within a VARBINARY column?
This is slow, clumsy and erronous...
The second thing is: .modify() will work against a real native XML only. Neither inboxXml.modify() nor CAST(inboxXml AS XML).modify() will work...
This is one more reason to change your column's type to XML...
Try this:
DECLARE #tbl TABLE(ID INT IDENTITY,YourXml VARBINARY(MAX));
DECLARE #SomeXML XML='<root><someNode someAttr="test">content</someNode></root>';
INSERT INTO #tbl VALUES(CAST(#SomeXML AS VARBINARY(MAX)));
--this works
SELECT ID
,YourXml
,CAST(YourXml AS XML)
FROM #tbl
WHERE ID=1;
--but this is not allowed
UPDATE #tbl SET CAST(YourXml AS XML).modify('replace value of (/root/someNode/#someAttr)[1] with "blah"')
WHERE ID=1
--What you can do:
DECLARE #intermediateXML XML= (SELECT CAST(YourXml AS XML) FROM #tbl WHERE ID=1);
SET #intermediateXML.modify('replace value of (/root/someNode/#someAttr)[1] with "blah"');
UPDATE #tbl SET YourXml=CAST(#intermediateXML AS VARBINARY(MAX)) WHERE ID=1;
--voila!
SELECT ID
,YourXml
,CAST(YourXml AS XML)
FROM #tbl
WHERE ID=1;

Performance difference between INSERT INTO table SELECT and SELECT INTO table [duplicate]

This question already has answers here:
INSERT INTO vs SELECT INTO
(11 answers)
Closed 6 years ago.
I have two queries.
First One is:
SELECT
Tbl.Col.value('ID[1]','INT') AS ID,
Tbl.Col.value('SPACE_CODE[1]', 'VARCHAR(100)') AS SPACE_CODE,
Tbl.Col.value('SPACE_TYPES[1]', 'INT') AS SPACE_TYPES,
Tbl.Col.value('IS_CORPORATE[1]', 'BIT') AS IS_CORPORATE,
Tbl.Col.value('IS_HOTELLING[1]', 'BIT') AS IS_HOTELLING,
Tbl.Col.value('AREA_NAME[1]', 'VARCHAR(50)') AS AREA_NAME,
Tbl.Col.value('OPERATION[1]', 'varchar(20)') AS OPERATION,
Tbl.Col.value('REMARKS[1]', 'varchar(200)') AS REMARKS,
CAST('' AS VARCHAR(20)) AS RESULT,
CAST('' AS VARCHAR(200)) AS COMMENTS
INTO #temp_space
FROM #XML_Data.nodes('//row') Tbl(Col)
Another query is:
CREATE TABLE #temp_space
(
id int identity(1,1),
AREA_NAME VARCHAR(20),
IS_CORPORATE BIT,
IS_HOTELLING BIT,
OPERATION VARCHAR(20),
REMARKS VARCHAR(200),
SPACE_CODE VARCHAR(100),
SPACE_TYPES INT,
RESULT VARCHAR(20),
COMMENTS VARCHAR(100)
)
INSERT INTO #temp_space(SPACE_CODE ,SPACE_TYPES ,IS_CORPORATE ,IS_HOTELLING,AREA_NAME ,OPERATION ,REMARKS )
SELECT
Tbl.Col.value('ID[1]','INT') AS ID,
Tbl.Col.value('SPACE_CODE[1]', 'VARCHAR(100)') AS SPACE_CODE,
Tbl.Col.value('SPACE_TYPES[1]', 'INT') AS SPACE_TYPES,
Tbl.Col.value('IS_CORPORATE[1]', 'BIT') AS IS_CORPORATE,
Tbl.Col.value('IS_HOTELLING[1]', 'BIT') AS IS_HOTELLING,
Tbl.Col.value('AREA_NAME[1]', 'VARCHAR(50)') AS AREA_NAME,
Tbl.Col.value('OPERATION[1]', 'varchar(20)') AS OPERATION,
Tbl.Col.value('REMARKS[1]', 'varchar(200)') AS REMARKS,
CAST('' AS VARCHAR(20)) AS RESULT,
CAST('' AS VARCHAR(200)) AS COMMENTS
FROM #XML_Data.nodes('//row') Tbl(Col)
First query is taking around 5 minutes to execute while second one is taking 3 seconds for the same number of records(around 2500).Can you please tell me why there is a difference in both the queries .
I know where to use both the queries,Just curious why the first one is taking too much time.
I need to validate each record from table table. Is there any alternative to loops and cursors.
I assume the performance difference because of the loggings and table configurations.
Select INTO: The select into creates a table with minimal configuration leaving the indexes and triggers (if any) and copy the information to the target table.
Insert Into: The insert into is applicable for a table which is already existing. The table may not be as simple as Select Into configuration.

Dynamically insert multiple xml data into sql server tables using single procedure

I need help to insert different format xml data into a SQL Server database using single procedure>
Ex:
<Items>
<item></item>
<item></item>
<item></item>
</Items>
<Products>
<product></product>
<product></product>
<product></product>
</Products>
So I need one procedure to insert the data into database taking xml as input. (i have nearly 20 xml formats), I will pass only one xml as input but not all.
Regards
Jayachandra
You may try somethings as:
create procedure spInsertData
(
#data xml
)
as
begin
set nocount on
if #data.exist('/Items') = 1
begin
insert into Items (Name)
select T.c.value('text()[1]', 'nvarchar(100)')
from #data.nodes('/Items/Item') T(c)
where not exists (select 1 from Items where Name = T.c.value('text()[1]', 'nvarchar(100)'))
return;
end
else if #data.exist('/Products') = 1
begin
insert into Products (Name)
select T.c.value('text()[1]', 'nvarchar(100)')
from #data.nodes('/Products/Product') T(c)
where not exists (select 1 from Products where Name = T.c.value('text()[1]', 'nvarchar(100)'))
return;
end
-- etc.
end

SQL Server: Output an XML field as tabular data using a stored procedure

I am using a table with an XML data field to store the audit trails of all other tables in the database.
That means the same XML field has various XML information. For example my table has two records with XML data like this:
1st record:
<client>
<name>xyz</name>
<ssn>432-54-4231</ssn>
</client>
2nd record:
<emp>
<name>abc</name>
<sal>5000</sal>
</emp>
These are the two sample formats and just two records. The table actually has many more XML formats in the same field and many records in each format.
Now my problem is that upon query I need these XML formats to be converted into tabular result sets.
What are the options for me? It would be a regular task to query this table and generate reports from it. I want to create a stored procedure to which I can pass that I need to query "<emp>" or "<client>", then my stored procedure should return tabular data.
does this help?
INSERT INTO #t (data) SELECT '
<client>
<name>xyz</name>
<ssn>432-54-4231</ssn>
</client>'
INSERT INTO #t (data) SELECT '
<emp>
<name>abc</name>
<sal>5000</sal>
</emp>'
DECLARE #el VARCHAR(20)
SELECT #el = 'client'
SELECT
x.value('local-name(.)', 'VARCHAR(20)') AS ColumnName,
x.value('.','VARCHAR(20)') AS ColumnValue
FROM #t
CROSS APPLY data.nodes('/*[local-name(.)=sql:variable("#el")]') a (x)
/*
ColumnName ColumnValue
-------------------- --------------------
client xyz432-54-4231
*/
SELECT #el = 'emp'
SELECT
x.value('local-name(.)', 'VARCHAR(20)') AS ColumnName,
x.value('.','VARCHAR(20)') AS ColumnValue
FROM #t
CROSS APPLY data.nodes('/*[local-name(.)=sql:variable("#el")]') a (x)
/*
ColumnName ColumnValue
-------------------- --------------------
emp abc5000
*/
Neither xyz432-54-4231 nor abc5000 is valid XML.
You can try to select only one particular format with a like statement, f.e.:
select *
from YourTable
where YourColumn like '[a-z][a-z][a-z][0-9][0-9][0-9][0-9]'
This would match 3 letters followed by 4 numbers.
A better option is probably to add an extra column to the table, where you save the type of the logging. Then you can use that column to select all "emp" or "client" rows.
An option would be to create a series of views that present the aduit table, per type in the relations that you're execpting
for example
select
c.value('name','nvarchar(50)') as name,
c.value('ssn', 'nvarchar(20)') as ssn
from yourtable
cross apply yourxmlcolumn.nodes('/client') as t(c)
you could then follow the same pattern for the emp
you could also create a view (or computed column) to identify each xml type like this:
select yourxmlcolumn.value('local-name(/*[1])', 'varchar(100)') as objectType
from yourtable
Use open xml method
DECLARE #idoc int
EXEC sp_xml_preparedocument #idoc OUTPUT, #xmldoc
SELECT * into #test
FROM OPENXML (#idoc, 'xmlfilepath',2)
WITH (Name varchar(50),ssn varchar(20)
)
EXEC sp_xml_removedocument #idoc
after you get the data in the #test
and you can manipulate this.
you may be put the diff data in diff xml file.

Resources