Stored procedure which writes xml into a DB, T-SQL - sql-server

I am trying to write a stored procedure which writes xml into a DB, T-SQL.
Here is my sample xml (which will have a significant number of <RECORD>s in prod environment):
<?xml version="1.0" encoding="windows-1251"?>
<DATA FORMAT_VERSION="1.0">
<RECORD>
<NAME>МІЖНАРОДНА ГРОМАДСЬКА ОРГАНІЗАЦІЯ МІЖНАРОДНА АКАДЕМІЯ БІОЕНЕРГОТЕХНОЛОГІЙ</NAME>
<SHORT_NAME>МАБЕТ</SHORT_NAME>
<EDRPOU>00011601</EDRPOU>
<ADDRESS>01001, м.Київ, Шевченківський район, ВУЛИЦЯ ПРОРІЗНА, будинок 8, офіс 426</ADDRESS>
<STAN>зареєстровано</STAN>
</RECORD>
</DATA>
I pass the path to the xml file in the #pathToXml parameter.
Here is my stored procedure:
CREATE PROCEDURE [dbo].[LegalContractorsDataSynchronize]
(
#pathToXml varchar
)
AS
BEGIN
BEGIN TRANSACTION
DELETE FROM [dbo].[LegalContractors];
INSERT INTO [dbo].[LegalContractors]([Code], [ShortName], [Name], [LegalAddress], [Status])
SELECT CONVERT([Code], [ShortName], [Name], [LegalAddress], [Status])
FROM OPENROWSET(BULK, #pathToXml, SINGLE_CLOB) as x
COMMIT TRANSACTION
END
I am using Entity Framework to call the stored procedure. The call just happens (with no errors), but the DB is not updated. I am very sure that I mistyped something in the INSERT statement. I followed this example.
Could someone point out how could I fill the three columns in my DB using the data from the respective elements in xml? The columns are Code, ShortName, Name, LegalAddress and Status.
UPDATE
After the answer being posted I tried the suggested solution. I am getting the error:
Msg 102, Level 15, State 1, Procedure LegalContractorsDataSynchronize, Line 15 [Batch Start Line 0]
Incorrect syntax near '#pathToXml'.
Here is my code:
CREATE PROCEDURE [dbo].[LegalContractorsDataSynchronize]
(
#pathToXml varchar
)
AS
BEGIN
BEGIN TRANSACTION
DELETE FROM [dbo].[LegalContractors];
;WITH XmlFile (xmlData) AS
(
SELECT TRY_CAST(BulkColumn AS XML)
FROM OPENROWSET(BULK #pathToXml, SINGLE_BLOB) AS x
)
INSERT INTO [dbo].[LegalContractors] ([Code], [ShortName], [Name], [LegalAddress], [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(100)') AS [Code]
, c.value('(SHORT_NAME/text())[1]','NVARCHAR(512)') AS [ShortName]
, c.value('(NAME/text())[1]','NVARCHAR(2048)') AS [Name]
, c.value('(ADDRESS/text())[1]','NVARCHAR(2048)') AS [LegalAddress]
, c.value('(STAN/text())[1]','NVARCHAR(100)') AS [Status]
FROM XmlFile CROSS APPLY xmlData.nodes('/DATA/RECORD') AS t(c);
COMMIT TRANSACTION
END

Please try the following. To the best of my knowledge, OPENROWSET() doesn't accept file name parameter as a variable.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (
ID INT IDENTITY PRIMARY KEY,
Code NVARCHAR(50) NOT NULL,
ShortName NVARCHAR(100) NOT NULL,
[Name] NVARCHAR(100) NOT NULL,
LegalAddress NVARCHAR(100) NOT NULL,
[Status] NVARCHAR(50) NOT NULL
);
-- DDL and sample data population, end
-- Method #1
-- XML file is hardcoded
;WITH XmlFile (xmlData) AS
(
SELECT TRY_CAST(BulkColumn AS XML)
FROM OPENROWSET(BULK 'c:\...\Ukraine.xml', /*CODEPAGE = '65001',*/ SINGLE_BLOB) AS x
)
INSERT INTO #tbl (Code, ShortName, [Name], LegalAddress, [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(50)') AS [Code]
, c.value('(SHORT_NAME/text())[1]','NVARCHAR(100)') AS [ShortName]
, c.value('(NAME/text())[1]','NVARCHAR(100)') AS [Name]
, c.value('(ADDRESS/text())[1]','NVARCHAR(100)') AS [LegalAddress]
, c.value('(STAN/text())[1]','NVARCHAR(50)') AS [Status]
FROM XmlFile CROSS APPLY xmlData.nodes('/DATA/RECORD') AS t(c);
-- test
SELECT * FROM #tbl;
-- Method #2
-- dynamic XML file name as a parameter
DECLARE #xml XML
, #sql NVARCHAR(MAX)
, #fileName VARCHAR(256) = 'c:\...\Ukraine.xml';
SET #sql = N'SELECT #xmlOut = XmlDoc FROM OPENROWSET (BULK ' + QUOTENAME(#fileName,NCHAR(39)) + ', SINGLE_BLOB) AS Tab(XmlDoc)';
EXEC master.sys.sp_executesql #sql, N'#xmlOut XML OUTPUT', #xmlOut = #xml OUTPUT;
INSERT INTO #tbl (Code, ShortName, [Name], LegalAddress, [Status])
SELECT c.value('(EDRPOU/text())[1]','NVARCHAR(50)') AS [Code]
, c.value('(SHORT_NAME/text())[1]','NVARCHAR(100)') AS [ShortName]
, c.value('(NAME/text())[1]','NVARCHAR(100)') AS [Name]
, c.value('(ADDRESS/text())[1]','NVARCHAR(100)') AS [LegalAddress]
, c.value('(STAN/text())[1]','NVARCHAR(50)') AS [Status]
FROM #xml.nodes('/DATA/RECORD') AS t(c);
-- test
SELECT * FROM #tbl;

Related

Adventure Works 2019: Adding New Person to Person.Person Table

Right now I am using 3 stored procedures to Add a person to the person.person table. I would like to cut this down to a single stored procedure to resolve this issue.
INSERT new GUID and DateModified to table Person.BusinessEntity
SELECT the auto generate BusinessEntityID form table Person.BusinessEntity
INSERT new Person to Person.Person Table
The Stored Procedures all use parameters which I pass via a C# application and I have confirmed that the a user is in fact added to the AdventureWorks2019 Db.
Procedure: Person.CreateNewBusinessEntity
INSERT INTO [Person].[BusinessEntity]
(
[BusinessEntity].rowguid
, [BusinessEntity].ModifiedDate
)
VALUES
(
#RowGUID
, GetDate()
)
Procedure: Person.GetBusinessEntityID
SELECT
[BusinessEntityID]
FROM
[AdventureWorks2019].[Person].[BusinessEntity]
WHERE [rowguid] = #RowGuid
Procedure: Person.CreateNewPerson
INSERT INTO [Person].[Person]
(
[BusinessEntityID]
,[PersonType]
,[NameStyle]
,[Title]
,[FirstName]
,[MiddleName]
,[LastName]
,[Suffix]
,[EmailPromotion]
,[AdditionalContactInfo]
,[Demographics]
,[rowguid]
,[ModifiedDate]
)
VALUES
(
#BusinessEntityID
, #PersonType
, #NameStyle
, #Title
, #FirstName
, #MiddleName
, #LastName
, #Suffix
, #EmailPromotion
, #AdditionalContactInfo
, #Demographics
, #RowGUID
, GetDate()
)
Any help here is appreciated. Thanks!
Thanks to HABO, I am now using this solution. Now I only need two procedures.
DECLARE #Inserted table ( [BusinessEntityID] int );
INSERT INTO [Person].[BusinessEntity]
(
[BusinessEntity].rowguid
, [BusinessEntity].ModifiedDate
)
OUTPUT inserted.[BusinessEntityID] INTO #Inserted([BusinessEntityID])
VALUES
(
#RowGUID
, GetDate()
)
The below answer shows you how to get the inserted id and add it to the next insert in side the Same SP.
USE AdventureWorks2012
GO
CREATE PROC CreateNewPerson
AS
BEGIN
DECLARE #OutputTbl TABLE ([BusinessEntityID] INT, ModifiedDate DATETIME)
DECLARE #BusinessEntityID AS INT
INSERT INTO [Person].[BusinessEntity]
(
[BusinessEntity].rowguid
, [BusinessEntity].ModifiedDate
)
--Get the output value inserted to a table.
OUTPUT inserted.[BusinessEntityID], inserted.ModifiedDate INTO
#OutputTbl([BusinessEntityID],[ModifiedDate])
VALUES
(
NEWID()
, GetDate()
)
--Assigned to a variable. You can get this using subquery as well inside the insert statment.
SELECT #BusinessEntityID = [BusinessEntityID] FROM #OutputTbl
INSERT INTO [Person].[Person]
(
[BusinessEntityID]
,[PersonType]
,[NameStyle]
,[Title]
,[FirstName]
,[MiddleName]
,[LastName]
,[Suffix]
,[EmailPromotion]
,[AdditionalContactInfo]
,[Demographics]
,[rowguid]
,[ModifiedDate]
)
VALUES
(
#BusinessEntityID
, #PersonType --These columns with # sign needed to be declared or supply values
, #NameStyle
, #Title
, #FirstName
, #MiddleName
, #LastName
, #Suffix
, #EmailPromotion
, #AdditionalContactInfo
, #Demographics
, NEWID() --This will generate a new GUID for each row.
, GetDate()
)
END
GO
Second way is using Scope_identity(). Replace the
"SELECT #BusinessEntityID = [BusinessEntityID] FROM #OutputTbl'
from below lines in the Sp will do the same thing for you.
SELECT #BusinessEntityID = SCOPE_IDENTITY() --[BusinessEntityID] FROM #OutputTbl
Select #BusinessEntityID

Sqlserver proc dynamic where condition

I want to construct select query with dynamic where condition and insert into one temp table.
for eg.,
ALTER PROCEDURE asp_My_Proc
(
#empName nvarchar(50),
#limit_Operator nvarchar(2), -- Possible values '>' or '<'
#limit_Value int
)
INSERT INTO #table1
Select c1,c2,c3 from Employee where empName LIKE '%' + COALESCE(#empName, empName) + '%' and limit > 100
Proc Execution script:
EXECUTE asp_My_Proc
'John'
, '>'
,10
GO
Limit_value condition, I have to add limit condition based on 'limit_Operator' variable. How to construct this dynamically.
...
WHERE (#limit_Operator = '>' and t.limit > #limit_value)
OR (#limit_Operator = '<' and t.limit < #limit_value)
Dynamic sql only it is possible and that is best way to implement this to avoid sql injection
CREATE TABLE #EMP (
EMPNO INT PRIMARY KEY,
ENAME VARCHAR(10),
JOB VARCHAR(9),
MGR INT NULL,
HIREDATE DATETIME,
SAL NUMERIC(7,2),
COMM NUMERIC(7,2) NULL,
DEPT INT)
INSERT INTO #EMP VALUES
(1,'JOHNSON','ADMIN',6,'12-17-1990',18000,NULL,4)
INSERT INTO #EMP VALUES
(2,'HARDING','MANAGER',9,'02-02-1998',52000,300,3)
INSERT INTO #EMP VALUES
(3,'TAFT','SALES I',2,'01-02-1996',25000,500,3)
INSERT INTO #EMP VALUES
(4,'HOOVER','SALES I',2,'04-02-1990',27000,NULL,3)
INSERT INTO #EMP VALUES
(5,'LINCOLN','TECH',6,'06-23-1994',22500,1400,4)
INSERT INTO #EMP VALUES
(6,'GARFIELD','MANAGER',9,'05-01-1993',54000,NULL,4)
INSERT INTO #EMP VALUES
(7,'POLK','TECH',6,'09-22-1997',25000,NULL,4)
DECLARE
#EMPNAME NVARCHAR(50)='JOH',
#LIMIT_OPERATOR NVARCHAR(2)='>', -- POSSIBLE VALUES '>' OR '<'
#LIMIT_VALUE INT=100
DECLARE #A VARCHAR(MAX)
SET #A =CONCAT('
SELECT * FROM #EMP
WHERE ENAME LIKE ''%'' + COALESCE(''',#EMPNAME,''', ENAME) + ''%'' AND SAL ',#LIMIT_OPERATOR,' ',#LIMIT_VALUE,'','')
--EXEC (#A)
SELECT #A

Issue with splitting values and passing to a stored procedure

Iam getting the below input parameter values in one of the stored procedure.
#DocKey1, #DocValue1,
#DocKey2, #DocValue2,
#DocKey3, #DocValue3,
#DocKey4, #DocValue4,
#DocKey5, #DocValue5
From this procedure iam calling another procedure to insert each pair of values.
Right now am calling the InsertDocValues stored procedure multiple times to insert each pair.
exec InsertDocValues #DocKey1, #DocValue1
exec InsertDocValues #DocKey2, #DocValue2
exec InsertDocValues #DocKey3, #DocValue3
exec InsertDocValues #DocKey4, #DocValue4
exec InsertDocValues #DocKey5, #DocValue5
Is there anyway i can pass the complete set of values to another procedure as below and then split each pair and insert
eg: #DocKey1, #DocValue1 and #DocKey2, #DocValue2 etc
#DocKey1, #DocValue1, #DocKey2, #DocValue2, #DocKey3, #DocValue3, #DocKey4, #DocValue4, #DocKey5, #DocValue5
Below is the procedure am using now to insert
Create PROCEDURE [dbo].[InsertDocValues]
(
#DocKey varchar(20),
#DocValue nvarchar(20)
)
AS
SET NOCOUNT ON;
BEGIN
INSERT INTO dbo.DocValues(
DocKey,
DocValue
)
VALUES(
#DocKey,
#DocValue
)
End
Please suggest
May be the below code would work for you.
Using user defined table type variable.
CREATE TYPE DocTable AS TABLE
(
DocKey int,
DocValue nvarchar(50)
);
GO
And then use this type to create required variable in first SP and pass the same to your second SP.
DECLARE #DocTable AS DocTable;
INSERT INTO #DocTable
SELECT #DocKey1, #DocValue1 UNION ALL
SELECT #DocKey2, #DocValue2 UNION ALL
SELECT #DocKey3, #DocValue3 UNION ALL
SELECT #DocKey4, #DocValue4 UNION ALL
SELECT #DocKey5, #DocValue5
You can create the above insert query dynamically also. There are so many ways to populate a table. So, use any one as you are getting output from your first SP.
And then call your Second SP.
EXEC [dbo].[InsertDocValues] #DocTable
Changes in second SP would be look like this.
Create PROCEDURE [dbo].[InsertDocValues]
(
#DocTable DocTable READONLY
)
AS
SET NOCOUNT ON;
BEGIN
INSERT INTO dbo.DocValues(
DocKey,
DocValue
)
SELECT
DocKey,
DocValue
FROM #DocTable
END
I'm getting the sense you have a string of pairs (key,value). Perhaps something like this:
Example
Declare #List varchar(max) = 'Key1:Value1,Key2:Value2'
Insert Into dbo.DocValues(DocKey,DocValue )
Select DocKey = left(RetVal,charindex(':',RetVal+':')-1)
,DocVal = stuff(RetVal,1,charindex(':',RetVal+':'),'')
From (
Select RetSeq = row_number() over (Order By (Select null))
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#List,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) A
The Data Inserted would be
DocKey DocVal
Key1 Value1
Key2 Value2

Get multiple values from XML field stored as Varchar(max)

I have this value in a varchar(max) column in SQL Server:
<VersionSeries><SeriesTypeIdList><int>3</int><int>4</int><int>2</int><int>29</int><int>31</int><int>32</int><int>39</int></SeriesTypeIdList></VersionSeries
I want to get the int values out into a table.
This is the code I have but it is only returning the first record it finds.
Declare #Version varchar(100)
Select #Version = '2016A Demo'
DECLARE #DataTable TABLE
(
Xml XML NOT NULL,
Code NVARCHAR(50) NULL
)
INSERT
INTO #DataTable(Xml)
SELECT
CONVERT(XML,CONVERT(NVARCHAR(max), Series))
FROM Version
where VersionName = #Version
Create table #SeriesCodes
(Code integer)
Insert Into #SeriesCodes
(Code)
SELECT
T.c.value('int[1]', 'nvarchar(50)') as Code
FROM #DataTable d
OUTER APPLY d.Xml.nodes('/VersionSeries/SeriesTypeIdList') T(c);
Select * from #SeriesCodes
This is a single statement to get what you want:
DECLARE #x2 XML = N'<VersionSeries><SeriesTypeIdList><int>3</int><int>4</int><int>2</int><int>29</int><int>31</int><int>32</int><int>39</int></SeriesTypeIdList></VersionSeries>';
SELECT t.c.query(N'.').value(N'(/*)[1]', N'int') AS [int_value]
FROM #X2.nodes(N'/VersionSeries/SeriesTypeIdList/*') AS [t]([c]);
Like this:
Declare #Version varchar(100)
Select #Version = '2016A Demo'
DECLARE #DataTable TABLE
(
Xml XML NOT NULL,
Code NVARCHAR(50) NULL
)
INSERT
INTO #DataTable(Xml)
values (N'<VersionSeries><SeriesTypeIdList><int>3</int><int>4</int><int>2</int><int>29</int><int>31</int><int>32</int><int>39</int></SeriesTypeIdList></VersionSeries>')
SELECT
T.c.value('.', 'nvarchar(50)') as Code
FROM #DataTable d
OUTER APPLY d.Xml.nodes('/VersionSeries/SeriesTypeIdList/int') T(c);

Insert a row to SQL table

I am writing a store procedure in T-SQL which inserts a row to the table, based on parameters
#UserName ,#CompanyName ,#RestName,#Desc
INSERT INTO Orders(UserId,CompanyId,RestId)
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id FROM UserNames,CompanyNames,RestNames
WHERE
UserNames.Name = #UserName AND
CompanyNames.Name = #CompanyName AND
RestNames.Name = #RestName
Besides the insert to the 3 columns above,I also want to insert the #Desc value.
I tried :
INSERT INTO Orders(UserId,CompanyId,RestId,Desc)
VALUES(
(SELECT UserNames.Id,CompanyNames.Id,RestNames.Id FROM UserNames,CompanyNames,RestNames
WHERE
UserNames.Name = #UserName AND
CompanyNames.Name = #CompanyName AND
RestNames.Name = #RestName),#Desc)
Only one expression can be specified in the select list when the subquery is not introduced with EXISTSt-
It doesn`t work giving the following error:
#UserName ,#CompanyName ,#RestName,#Desc
INSERT INTO Orders(UserId,CompanyId,RestId, Desc_Column)
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id , #Desc --<-- Just SELECT that variable
FROM UserNames,CompanyNames,RestNames -- in your select statement.
WHERE UserNames.Name = #UserName
AND CompanyNames.Name = #CompanyName
AND RestNames.Name = #RestName
Retrieve ID Values Inserted
DECLARE #t TABLE (ID INT); --<-- Declare a table variable
INSERT INTO Orders(UserId,CompanyId,RestId, Desc_Column)
OUTPUT Inserted.ID INTO #t --<-- use OUTPUT, get values from INSERTED Table
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id , #Desc --and insert them into your table variable
FROM UserNames,CompanyNames,RestNames
WHERE UserNames.Name = #UserName
AND CompanyNames.Name = #CompanyName
AND RestNames.Name = #RestName
/*At last just simply select from that table variable to get the inserted IDs*/
SELECT * FROM #t

Resources