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);
Related
I'm looking to pass my database name as a parameter to the stored procedure, and I'm looking to use it in the where condition to set the database of the stored procedure. But I get an error:
Incorrect syntax near '.'
Sample Code
Create proc [dbo].[stored_procedure_one]
#variable1 int,
#dbname varchar(10)
as
begin
select *
from #dbname..table_name
End
Can someone suggest me how to solve this?
You will need to use dynamic sql for this something like this.....
Create proc [dbo].[stored_procedure_one]
#variable1 int,
#dbname SYSNAME --<-- use appropriate data type for object names
as
begin
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' select * from ' + QUOTENAME(#dbname) + N'..table_name'
Exec sp_executesql #Sql
End
Also use QUOTENAME() function to protect yourself against possible sql-injection attack.
Just to offer an alternative, it's fun to note that EXEC can take a string as the thing to execute, so for example:
DECLARE #sp nvarchar(255) = N'sys.sp_who2';
EXEC #sp;
It can also take parameters, e.g.
DECLARE #sp nvarchar(255) = N'sys.sp_who2';
EXEC #sp 'active';
So we can dynamically build the context where we run a command by using:
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
DECLARE #sql nvarchar(max) = N'SELECT DB_NAME();';
EXEC #context #sql;
And you can pass parameters, too:
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
DECLARE #sql nvarchar(max) = N'SELECT DB_NAME(), #x;';
EXEC #context #sql, N'#x int', 5;
This approach really simplifies things like concatenating the database name all over the place, avoiding db-specific functions like object_name, and ensures that your entire command runs in that other database. You can also do it across linked servers, e.g.:
DECLARE #server sysname = N'linked_server';
DECLARE #dbname sysname = N'tempdb';
DECLARE #context nvarchar(1000) = QUOTENAME(#server)
+ N'.' + QUOTENAME(#dbname)
+ N'.sys.sp_executesql';
...
I'm creating a script to read a .txt file, I can make it work if I enter the address manually, but in that case I need to create a dynamic address concatenating the location of the file + its name.
I've tried several things like turning the whole procedure into string to execute with the Exec() command, the only way I found it would be concatenating the string in a previously declared variable, the problem is that SQL does not recognize this variable.
DECLARE #JSON VARCHAR(MAX) -- get json from file
DECLARE #item_name varchar(15) = 'fileName' --random file name from procedure
DECLARE #path varchar(50) = 'C:\Users\Peter\Documents\JsonFiles\'+ #item_name+ '.json' -- path of the file
SELECT #JSON = BulkColumn
FROM OPENROWSET
(BULK #path , SINGLE_CLOB) --the problem is here, SQL Server can't find #path
AS j...
I know I'm forgetting something, I'm just out of ideas for now.
Who can help me, please.
Edit: i got the solution, but there is another problem:
After creating the dynamic sql the #item_name worked like a charm, but now if i convert the #JSON like convert(varchar(max),#JSON) the sql variable will be empty, i'm using Print(#sql) to make some tests, and if i do Exec(#sql) nothing happens.
I've shortened the code a bit to fit here, and thanks so much for your time, you guys helped me so much.
DECLARE #JSON VARCHAR(MAX)
DECLARE #item_name varchar(15) = 'fileName'
DECLARE #path varchar(60) = 'C:\Users\Peter\Documents\JsonFiles\'+#item_name+'.json'
DECLARE #sql varchar(max)
set #sql =
'
SELECT #JSON = BulkColumn
FROM OPENROWSET
(BULK ' + convert(varchar(60),#path) + ', SINGLE_CLOB)
AS j
INSERT INTO items
SELECT visible, last_update, item = ' + convert(varchar(15),#item_name) + '
FROM OPENJSON (#JSON , ''$.payload.orders'')
WITH (
visible varchar(7) ''$.visible'',
last_update datetimeoffset ''$.last_update'',
item varchar(10) )'
Print(#sql)
I found the issue, just declare #JSON after set #sql
DECLARE #item_name varchar(15) = 'fileName'
DECLARE #path varchar(60) = 'C:\Users\Peter\Documents\JsonFiles\'+#item_name+'.json'
DECLARE #sql varchar(max)
set #sql =
'
DECLARE #JSON VARCHAR(MAX)
SELECT #JSON = BulkColumn
FROM OPENROWSET
(BULK ' + convert(varchar(60),#path) + ', SINGLE_CLOB)
AS j
INSERT INTO items
SELECT visible, last_update, item = ' + convert(varchar(15),#item_name) + '
FROM OPENJSON (#JSON , ''$.payload.orders'')
WITH (
visible varchar(7) ''$.visible'',
last_update datetimeoffset ''$.last_update'',
item varchar(10) )'
Print(#sql)
Essentially I am trying to use SQL to query XML files within a folder. The code to query the XML works fine on its own but once I turned it into a string to allow for expressions in 'openrowset' it keeps saying throwing out an error message saying it cannot find #x and that 'Must declare the scalar variable'. I understand there is also sp_executesql but I don't really understand how that works and why it would be needed.
Thanks in advance!
drop table #tmp
CREATE TABLE #tmp(files VARCHAR(100));
INSERT INTO #tmp
EXEC xp_cmdshell 'dir /B "C:\Users\USER\A\B"';
While (Select Count(*) From #tmp where files is not null) > 0
Begin
DECLARE #fileName varchar(max)
DECLARE #filepath varchar(max)
Declare #sql nvarchar(max)
Select Top 1 #fileName = files From #tmp
Set #filepath = 'C:\Users\USER\990\A\B\' + #filename
DECLARE #n1 varchar(max)
DECLARE #n2 varchar(max)
Set #n1 = 'http://www.namespace1.com/file'
Set #n2 ='http://www.namespac2.com/XMLSchema'
SET #sql = '
DECLARE #x xml
#x = R
FROM Openrowset(Bulk ''' + #filepath + ''' , Single_Blob) AS ReturnData(R);
WITH XMLNAMESPACES(DEFAULT '''+#n1+'''
,'''+#n2+''' AS xsi)
INSERT INTO [dbo].[IRS]
([A]
,[B]
,[C]
)
Select
ct.value('''+'(../../Data/A/)[1]'+''','''+'varchar(max)'+''')
,ct.value('''+'(../../Data/B)[1]'+''','''+'varchar(max)'+''')
,ct.value('''+'(../../Data/C/)[1]'+''','''+'varchar(max)'+''')
FROM #x.nodes('''+'/Return/ReturnData/IRS990ScheduleH'+''') AS A(ct)
'
exec(#sql)
Delete from #tmp Where files = #FileName
End
I think your error happens here:
DECLARE #x xml
#x = Something
Either use
DECLARE #x XML=SomeXml;
or
DECLARE #x XML=(SELECT SomeXml FROM SomeWhere);
or
DECLARE #x xml;
and then either
SET #x=SomeXml;
or
SET #x=(SELECT SomeXml FROM SomeWhere);
or
SELECT #x=SomeXml FROM SomeWhere;
How do I create variables that are specified once and then used in queries later in a script? These variables may be used multiple times in a query, and in multiple queries in a script. I use #x as such a variable in the examples below.
What I want to do is something like:
Declare #Query nvarchar(1000)
Declare #x nvarchar(40)
Set #x = 'test'
Set #Query = 'Select [Name]
, ' + #x + ' as [TestCase]
From mytable'
Exec (#Query)
-- returns "Invalid column name 'test'"
Which returns the error mentioned above. I would like it to achieve the equivalent of:
Declare #Query nvarchar(1000)
Declare #x nvarchar(40)
Set #x = 'test'
Set #Query = 'Select [Name]
, ''test'' as [TestCase]
From mytable'
Exec (#Query)
-- Returns e.g.
-- Name TestCase
-- Alice Test
-- Bob Test
I also note that the following doesn't work and returns the same error as the first:
Declare #Query nvarchar(1000)
Declare #x nvarchar(40)
Set #x = 'test'
Set #Query = 'Select [Name]
, ' + 'test' + ' as [TestCase]
From mytable'
Exec (#Query)
-- returns "Invalid column name 'test'"
Based on the error and since I'm not trying to use the #x as a column name, but just as a variable, I assume I'm using an invalid implementation of a variable.
Since you're not trying to use a variable as a column name, you do not need to use dynamic SQL at all. (Which is a Good Thing(TM) since dynamic SQL should only be used with a great deal of caution due to it being a great attack surface.)
A simple:
declare #x nvarchar(40)
set #x = 'test'
select [Name], #x as TestCase
from mytable
will do.
That being said, if you have a use case for dynamic SQL (again the particular query in question here does not but perhaps an ad-hoc query is being passed in to the procedure), the thing to do would be to pass your variable as a parameter to the query via sp_executesql. This is akin to creating a stored procedure with parameters:
declare #x nvarchar(40)
declare #query nvarchar(1000)
set #x = 'test'
set #query = 'select [Name], #x as TestCase from mytable'
exec sp_executesql #query, N'#x nvarchar(1000)', #x
You were missing quotes. Thats it. Try below code.
Declare #Query nvarchar(1000)
Declare #x nvarchar(40)
Set #x = 'test'
Set #Query = 'Select [Name]
, ''' + #x + ''' as [TestCase]
From mytable'
Exec (#Query)
Declare #Query nvarchar(1000)
Declare #x nvarchar(40)
Set #x = 'test'
Set #Query = 'Select [Name],'++''''+#x+''''+ ' as [TestCase]
From mytable'
print #query
Output:
Select [Name],'test' as [TestCase]
From mytable
I have a dynamic query which reads like this
Alter PROCEDURE dbo.mySP
-- Add the parameters for the stored procedure here
(
#DBName varchar(50),
#tblName varchar(50)
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
declare #string as varchar(50)
declare #string1 as varchar(50)
set #string1 = '[' + #DBName + ']' + '.[dbo].' + '[' + #tblName + ']'
set #string = 'select * from ' + #string1
exec #string
END
I am calling like this
dbo.mySP 'dbtest1','tblTest'
And I am experiencing an error
"Msg 203, Level 16, State 2, Procedure mySP, Line 27
The name 'select * from [dbtest1].[dbo].[tblTest]' is not a valid identifier."
What is wrong? and How to overcome?
Thanks in advance
It thinks that the contents of #string refer to a stored procedure name. You need to put
EXEC (#string)
or better use the stored procedure sp_executesql
You should also set up some guard code to check that the values you are passing in are the names of real tables and databases. You can query the views in the INFORMATION_SCHEMA to validate the input.
You can read more on safer dynamic SQL on my blog.
Change
exec #string
To
exec(#string)
Here's a working SP I just tested:
CREATE PROCEDURE [dbo].[test]
#DBName varchar(50),
#tblName varchar(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #string AS VARCHAR(50)
DECLARE #string1 AS VARCHAR(50)
SET #string1 = '[' + #DBName + '].[dbo].[' + #tblName + ']'
SET #string = 'select * from ' + #string1
EXEC(#string)
END
if you use EXEC as:
EXEC #String
it is trying to run a procedure with the name contained within the #String variable. try it out:
create procedure TestProc
as
print 'you called TestProc!'
go
declare #string varchar(20)
set #string='TestProc'
exec #string
if you use EXEC as:
EXEC (#Query)
you run the sql within the #Query variable, try it out:
DECLARE #Query varchar(50)
set #Query='Print ''just ran it!'''
EXEC (#Query)
ALTER PROCEDURE test_sp
-- Add the parameters for the stored procedure here
(
#DBName varchar(50),
#tblName varchar(50)
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
-- Insert statements for procedure here
declare #string as varchar(100)
declare #string1 as varchar(50)
set #string1 = '[' + #DBName + ']' + '.[dbo].' + '[' + #tblName + ']'
Print #string1
set #string = 'select * from' + #string1
Print #string
exec (#string)
SET NOCOUNT OFF
END