SQL can not find the variable? - sql

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)

Related

T-SQL Setting a scalar variable with the value of another scalar variable

Im creating a stored procedure that retrieves data to fill a radar chart. It worked pretty well using static tables und rows like this:
(This is just a piece of the code)
SELECT
#aAvg = CAST(AVG(1. * foerderpy_1617) as DECIMAL(18,4)),
#aMin = CAST(MIN(1. * foerderpy_1617) as DECIMAL(18,4)),
#aMax = CAST(MAX(1. * foerderpy_1617) as DECIMAL(18,4))
FROM foerderpy a WHERE SUBSTRING(a.BSN,3,1) = 'g';
But now i want a dynamic sql. I want the stored procedure to always take the latest row of my table:
(These are just pieces of the code)
DECLARE #SQL AS NVARCHAR(MAX);
DECLARE #aAvg AS NVARCHAR(MAX);
DECLARE #aMin AS NVARCHAR(MAX);
DECLARE #aMax AS NVARCHAR(MAX);
DECLARE #tabname SYSNAME;
DECLARE #coluname SYSNAME;
DECLARE #counter INTEGER;
SET #tabname = 'foerderpy'
SET #counter = (
SELECT MAX(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tabname
GROUP BY TABLE_NAME)
SET #coluname = (
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tabname AND
ORDINAL_POSITION = #counter)
SET #aAvg = (SELECT CAST(AVG(1. * #coluname) as DECIMAL(18,4))FROM #tabname a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING(#restriction,3,1))
At the last line (the SET #aAvg), the stored procedure stops working and sql tells me "i have to declare #tabname", although i obv. declared it above. What is the problem im missing? Is it even possible to do what im trying?
The rest of the Code isn't causing any problems so i left it out. I need the #aAvg to calculate later in the procedure.
You need to run your last query using EXECUTE because EXECUTE:
Executes a command string or character string within a Transact-SQL batch
So you have to change the last line of your procedure in a way that the query is written in a string and called by execute.
DECLARE #sql VARCHAR(max) = 'SELECT CAST(AVG(1. * ' + #coluname + ') as DECIMAL(18,4))FROM '+ #tabname +' a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING('+#restriction+',3,1)';
EXECUTE(#sql);
If you would like to save the value in your variable #aAvg, you can use sp_executesql with an out parameter, this way:
DECLARE #sql VARCHAR(max) = 'SELECT CAST(AVG(1. * ' + #coluname + ') as DECIMAL(18,4))FROM '+ #tabname +' a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING('+#restriction+',3,1)';
exec sp_executesql #sql, N'#aAvg decimal(18,4) out', #aAvg out
select #aAvg

Looping through XMLs in folder using openrowset / "Must declare the scalar variable" Issue

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;

Issue with Filestream Filetable not computing file_type column on insert

I am having a problem with my filestream filetable, when I run an insert statement on it the file saves, but the file_type column is null (and obviously cannot be updated manually given that it's a computed column)
DECLARE #counter INT, #maxPolicySectionId INT
DECLARE #name NVARCHAR(128)
DECLARE #file_stream VARBINARY(MAX)
DECLARE #path NVARCHAR(256)
DECLARE #command NVARCHAR(MAX)
DECLARE #fileTable TABLE(
name NVARCHAR(50),
filePath NVARCHAR(128),
polSecId INT
);
--...Populate #fileTable from database table...--
SELECT #counter = MIN(policySectionId), #maxPolicySectionId = MAX(policySectionId) FROM tblPolicySection
SET #name = (SELECT name FROM #fileTable WHERE polSecId = #counter)
SET #path = (SELECT filePath FROM #fileTable WHERE polSecId = #counter)
SET #path = REPLACE(#path, '192.168.0.6', 'meta-filesrv')
--SET #file_type in Filetable somehow.. unable to perform UPDATE because it's a computed column
SET #command = N'SELECT #file_stream1 = CAST(bulkcolumn AS varbinary(MAX))
from OPENROWSET(BULK ''' + #path + ''',
SINGLE_BLOB) AS x'
EXEC sp_executesql #command, N'#file_stream1 VARBINARY(MAX) OUTPUT',#file_stream1 = #file_stream OUTPUT
IF #path IS NOT NULL AND #name IS NOT NULL
BEGIN
INSERT INTO BlobDocStore(
name,
file_stream
)
SELECT
#name,
#file_stream
END
SET #counter = #counter + 1
As you can see I am passing a stream into the insert statement which should work to the best of my understanding. I'm hoping someone has had some experience with this in the past. Thanks.
The file_type is derived from the specified file name. If the file_type column is NULL, this suggests the #name value does not have a file extension.

SQL add a variable to a query

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

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);