Stored Procedure to Import XML into SQL Server - sql

I'm trying to import XMLs into SQL Server. If I print the below #command, I can copy and paste into SSMS and the code runs perfectly. However, I get the following error if I try to run this as an sp.
exec etl.import_xml 'c:\xml_file.xml'
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#stream".
ALTER PROCEDURE etl.import_xml (
#file_name VARCHAR(1000)
) AS
BEGIN
DECLARE #command NVARCHAR(1000)
DECLARE #stream VARCHAR(MAX)
SET #command = N'SELECT #stream = CAST(BulkColumn AS VARCHAR(MAX))
FROM OPENROWSET(BULK ''' + #file_name + ''', SINGLE_BLOB) AS x;'
--EXEC sp_executesql #command, N'#file_stream_out VARCHAR(MAX) OUTPUT', #stream_out = #stream OUTPUT
EXECUTE(#command)
SET #stream = dbo.strip_xmlns(#stream)
UPDATE swf.etl.raw_data
SET xml_string = CONVERT(XML, #stream)
WHERE xml_file_name = #file_name
END

As per Dale K, I need to declare everything within the command string. Obviously there are still issues with SQL injection, however no user will have access to this proc and will only be run on an automation server.
ALTER PROCEDURE etl.import_xml (
#file_name VARCHAR(1000)
) AS
BEGIN
DECLARE #command NVARCHAR(1000)
DECLARE #stream VARCHAR(MAX)
SET #command = N'DECLARE #stream VARCHAR(MAX) SELECT #stream = CAST(BulkColumn AS VARCHAR(MAX))
FROM OPENROWSET(BULK ''' + #file_name + ''', SINGLE_BLOB) AS x;
SET #stream = dbo.strip_xmlns(#stream)
UPDATE swf.etl.raw_data
SET xml_string = CONVERT(XML, #stream)
WHERE xml_file_name = ''' + #file_name + '''
'
EXECUTE(#command)
END

Here is how to do it with an XML file name as a parameter.
SQL
DECLARE #xml XML
, #sql NVARCHAR(MAX)
, #fileName VARCHAR(256) = 'e:\Temp\Diego.xml';
SET #sql = N'SELECT #xmlOut = XmlDoc FROM OPENROWSET (BULK ' + QUOTENAME(#fileName,NCHAR(39)) + ', SINGLE_BLOB) AS Tab(XmlDoc)';
EXEC sys.sp_executesql #sql, N'#xmlOut XML OUTPUT', #xmlOut = #xml OUTPUT;
SELECT #xml;

Related

How to use a variable in Openrowset command

I am trying to use a variable filepath in a SQL Openrowset command. I'm aware that it can't explicitly accept a variable and that I need to make use of dynamic SQL.
What currently works -
SELECT #file_stream = CAST(bulkcolumn AS VARBINARY(MAX))
FROM OPENROWSET(BULK 'C:\Temp\print4.pdf', SINGLE_BLOB) AS x
However if I try to use my variable filepath
declare #file_stream VARBINARY(MAX)
declare #filePath NVARCHAR(128)
set #filePath = 'C:\Temp\print4.pdf'
set #command = N'SELECT #file_stream = CAST(bulkcolumn AS varbinary(MAX))
from OPENROWSET(BULK ' + #filePath + ',
SINGLE_BLOB) ROW_SET'
EXEC sp_executesql #command, #filePath, #file_stream;
I get the error 'Msg 137, Level 15, State 2, Line 15
Must declare the scalar variable "#filePath".'
I'm sure this is an issue of syntax but haven't been able to figure out how it should be formatted yet.
Change your script like below.
DECLARE #file_stream VARBINARY(MAX)
DECLARE #command nvarchar(1000)
DECLARE #filePath NVARCHAR(128)
set #filePath = 'C:\Temp\print4.pdf'
set #command = N'SELECT #file_stream1 = CAST(bulkcolumn AS varbinary(MAX))
from OPENROWSET(BULK ''' + #filePath + ''',
SINGLE_BLOB) ROW_SET'
EXEC sp_executesql #command, N'#file_stream1 VARBINARY(MAX) OUTPUT',#file_stream1 =#file_stream OUTPUT
select #file_stream
Sample Output :

How to pass a string variable in BULK LOAD command in SQL

I want to pass the file name as a variable in SQL BULK load. But I am getting a syntax error. If someone can help that will be great. Below is the code that I wrote.
DECLARE #XML AS XML,#File VARCHAR(200)
SELECT #File='C:\xyz.xml'
SELECT #XML= CONVERT(XML, BulkColumn) FROM OPENROWSET(BULK #File, SINGLE_BLOB) AS x
I can not hardcode #File like BULK 'C:\xyz.xml' as the file name will change frequently.
You can't parametrise it. So use dynanic SQL
DECLARE #XML AS XML, #File VARCHAR(200);
DECLARE #SQL nvarchar(max);
SET #File='C:\xyz.xml';
SET #SQL = 'SELECT #XML= CONVERT(XML, BulkColumn) FROM OPENROWSET(BULK ''' + #File + ''', SINGLE_BLOB) AS x';
EXEC sys.sp_executesql #SQL, N'#XML xml', #XML;
Editing your answer as it was not giving #XML value
DECLARE #XML AS XML, #File VARCHAR(200);
DECLARE #SQL nvarchar(max);
SET #File='C:\xyz.xml';
SET #SQL = 'SELECT #XML_OUT= CONVERT(XML, BulkColumn) FROM OPENROWSET(BULK ''' + #File + ''', SINGLE_BLOB) AS x';
EXEC sys.sp_executesql #SQL, N'#XML xml OUT', #XML_OUT=#XML OUTPUT;

Create SQL Server stored procedure with stock data

I am trying to turn some code I have from SQL Server into a stored procedure with parameters that I can pass through but I am not sure how to do this. I want the 4 letter stock symbol of the URL to be a variable so that I can pass through different symbols, I also need this code to work as a stored procedure.
GOOG is where I need the variable.
http://finance.yahoo.com/webservice/v1/symbols/GOOG/quote'
--RSS FEED
DECLARE #docHandle INT;
DECLARE #xmlData XML;
DECLARE #URL NVARCHAR(255);
DECLARE #file NVARCHAR(255);
DECLARE #cmd NVARCHAR(255);
DECLARE #sql NVARCHAR(255);
DECLARE #tXML TABLE(data XML);
SET #URL = 'http://finance.yahoo.com/webservice/v1/symbols/GLUU/quote';
SET #file = 'c:\temp\quotes.xml';
-- Downloading the data
SET #cmd = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell (new-object System.Net.WebClient).DownloadFile( ''' + #URL + ''',''' + #file + ''' )'
EXEC master.dbo.xp_cmdshell #cmd, no_output
-- Loading the Downloaded File into the XML variable
SET #sql = 'SELECT BulkColumn FROM OPENROWSET( BULK ''' + #file + ''', SINGLE_BLOB ) AS a'
INSERT #tXML EXEC(#sql);
SELECT #xmlData = data from #tXML
-- Preparing the Relational Table from the XML variable
EXEC sp_xml_preparedocument #docHandle OUTPUT, #xmlData;
INSERT INTO tblstockdata ([Name], [Price], [Symbol], [TS], [Type], [Volume])
SELECT * FROM OPENXML(#docHandle, N'//list/resources/resource')
WITH ( Name VARCHAR(10) 'field[#name="name"]',
Price VARCHAR(10) 'field[#name="price"]',
Symbol VARCHAR(10) 'field[#name="symbol"]',
TS VARCHAR(10) 'field[#name="ts"]',
Type VARCHAR(10) 'field[#name="type"]',
Volume VARCHAR(10) 'field[#name="volume"]');
EXEC sp_xml_removedocument #docHandle;
Thanks!
:)
You have working SQL, which is the tough part. Now bracket your working SQL with
CREATE PROCEDURE <schema>.<Name>(
#param <type>
) as begin
and
end
and you should be good to go. For starters you can use dbo as the value of <schema> until you need to use a non-default schema. It is recommended that you not use the prefix "sp" or "xp" for your procedure name as these are used to identify system and extended procedures by SQL Server.
If you need additional parameters they can be added to the parameter list using a comma as the delimiter.
Below is a stored procedure example gleaned from your code that takes the needed symbol parameter. Here, I used the XML data type methods rather than sp_xml_preparedocument. If multiple users might call this stored procedure at the same time, I suggest you generate a unique name for the file an delete afterwards. You might consider a CLR stored procedure to avoid both the temporary file and OPENROWSET ugliness.
CREATE PROC dbo.usp_GetEquityQuote
#symbol varchar(10)
AS
DECLARE #URL NVARCHAR(255);
DECLARE #file NVARCHAR(255);
DECLARE #cmd NVARCHAR(255);
DECLARE #sql NVARCHAR(255);
DECLARE #tXML TABLE(data XML);
SET #URL = 'http://finance.yahoo.com/webservice/v1/symbols/' + #symbol + '/quote';
SET #file = 'c:\temp\quotes.xml';
-- Downloading the data
SET #cmd = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell (new-object System.Net.WebClient).DownloadFile( ''' + #URL + ''',''' + #file + ''' )';
EXEC master.dbo.xp_cmdshell #cmd, no_output;
-- Loading the Downloaded File into the XML variable
SET #sql = 'SELECT BulkColumn FROM OPENROWSET( BULK ''' + #file + ''', SINGLE_BLOB ) AS a';
INSERT #tXML EXEC(#sql);
SELECT data from #tXML ;
SELECT
resources.resource.value('field[#name="name"][1]', 'varchar(10)') AS name
,resources.resource.value('field[#name="price"][1]', 'decimal(18,4)') AS price
,resources.resource.value('field[#name="symbol"][1]', 'varchar(10)') AS symbol
,resources.resource.value('field[#name="ts"][1]', 'bigint') AS ts
,resources.resource.value('field[#name="type"][1]', 'varchar(10)') AS type
,resources.resource.value('field[#name="volume"][1]', 'bigint') AS volume
FROM (SELECT data FROM #tXML) AS tXML(data)
CROSS APPLY data.nodes('/list/resources/resource') AS resources(resource);

T-SQL, Load XML data into a local variable

I would like to know, how can I load the XML content from an arbitrary file into a local variable?
This works for a fixed file:
DECLARE #xml XML
SET #xml =
(
SELECT *
FROM OPENROWSET(BULK 'C:\data.xml', SINGLE_BLOB) AS data
)
However, I would like to load the data from any arbitrary file.
This does not work (as BULK seems to only support String arguments)
DECLARE #file NVARCHAR(MAX) = 'C:\data.xml'
DECLARE #xml XML
SET #xml =
(
SELECT *
FROM OPENROWSET(BULK #file, SINGLE_BLOB) AS data
)
I've also tried the following (without success, as the local variable (#xml) seems to be out of scope when the EXEC is performed):
DECLARE #file NVARCHAR(MAX) = 'C:\data.xml'
DECLARE #xml XML
DECLARE #bulk NVARCHAR(MAX) = 'SET #xml = (SELECT * FROM OPENROWSET(BULK ''' + #file + ''', SINGLE_BLOB) AS data)'
EXEC (#bulk)
I'm guessing I need to use a temporary table, but how?
Found a solution:
DECLARE #results table (result XML)
DECLARE #sqlstmt NVARCHAR(MAX)
SET #sqlstmt= 'SELECT * FROM OPENROWSET ( BULK ''' + #file + ''', SINGLE_CLOB) AS xmlData'
INSERT INTO #results EXEC (#sqlstmt)
SELECT #xml = result FROM #results
you can also use sp_executesql:
declare #stmt nvarchar(max), #xml xml
select #stmt = '
set #xml = (select * from openrowset(bulk ''' + #file + ''', single_clob) as data)
'
exec dbo.sp_executesql
#stmt = #stmt,
#params = '#xml xml output',
#xml = #xml output

How to store results of dynamic SQL into a variable

is there a way to store the results of an exec statement in a varchar?
DECLARE #TableName varchar(100)
DECLARE #ExecStatement varchar(max)
DECLARE #PromotionXML varchar(max)
SET #TableName = 'Feeds'
Set #ExecStatement = (
'
SET #PromotionXML = (
SELECT
*
FROM
' + #TableName + ' for xml auto, elements
)'
)
exec #ExecStatement
select #PromotionXML
You need to use sp_executesql, not EXEC, since you need to treat the inner variable as an output parameter (which you can't do with EXEC). Also all of these parameters should be NVARCHAR, not VARCHAR (though I'm curious why you aren't returning the xml as XML).
DECLARE
#TableName NVARCHAR(512),
#sql NVARCHAR(MAX),
#xml NVARCHAR(MAX);
SET #TableName = N'dbo.Feeds';
SET #sql = N'SELECT #xml = CONVERT(NVARCHAR(MAX), (
SELECT * FROM ' + #TableName + ' FOR XML AUTO, ELEMENTS));';
EXEC sp_executesql #sql, N'#xml NVARCHAR(MAX) OUTPUT', #xml OUTPUT;
SELECT #xml;