OPENQUERY(SERVERNAME, STOREDPROCEDURE) Syntax error - sql

This is my code
DECLARE #stringvariable nvarchar(200) = 'Hello';
DECLARE #sql nvarchar(2000) = SELECT * INTO ##global FROM OPENQUERY(DB1, ''EXEC GETCASE ''' + #stringvariable + ''''')'
Printing #sql returns a correctly formatted query, however SQL Server doesn't like #stringvariable and returns an error
Msg 102, Level 15, State 1, Line 11
Incorrect syntax near 'Hello'.
Here is what the outputted query looks like
SELECT * INTO ##global FROM OPENQUERY(DB1, 'EXEC GETCASE 'Hello'')
How can I avoid this error? It seems like because my stored procedure takes a string parameter, it's throwing off the query. I've read that OPENQUERY does not support variables, but I've parameter the variable so it should work?
Appreciate your help!

The stored procedure exists in a database and a schema. You need to supply those. Supposing database db_name and schema schema_name:
DECLARE #stringvariable nvarchar(200) = 'Hello';
SET #stringvariable=REPLACE(#stringvariable,'''',''''''''''); -- doubly doubled single quotes for the dynamic statement
DECLARE #sql nvarchar(2000) = 'SELECT * INTO ##global FROM OPENQUERY(DB1, ''SET FMTONLY OFF;EXEC db_name.schema_name.GETCASE ''''' + #stringvariable + ''''''')';
I've also made sure single quotes are properly escaped in the #stringvariable.
It's also likely you need to start the query with SET FMTONLY OFF; so I've added that.
Update: To test this I created following simple procedure on a linked server local_server in database TEST_TT
CREATE PROCEDURE [dbo].[tst]
#i VARCHAR(128)
AS
SELECT #i AS field;
I then ran the following:
DECLARE #var VARCHAR(128)='TT.';
SET #var=REPLACE(#var,'''',''''''''''); -- doubly doubled single quotes for the dynamic statement
DECLARE #stmt VARCHAR(4000)='SELECT * INTO ##tt FROM OPENQUERY(local_server,''SET FMTONLY OFF;EXEC TEST_TT.dbo.tst '''''+#var+''''''');';
EXEC (#stmt);
SELECT * FROM ##tt;
DROP TABLE ##tt;
And I received the results. I count 7 (!!) single quotes at the end of the query... yuck! Updated original part with the same number of quotes.

Related

Avoid using dynamic from clause

How can i avoid using dynamic from clause? Even if i don't know the database name, i prefer to use a static statement, like this:
select *
into #tempTable
from #DBName.Invoices
where InvoiceId = 5.
I got this error: Msg 102, Level 15, State 1, Line 6
Incorrect syntax near '.'.
I need to use select into clause because the column names may be different from each databases;
Thanks!
Unfortunately you will have to use dynamic SQL for this, see below for an example
Declare #DBNAME NVARCHAR(MAX) = 'xxx'
Declare #SQL NVARCHAR(MAX) ='select *
into #tempTable
from ' + #DBName + '.Invoices
where InvoiceId = 5.'
execute sp_executesql #SQL
How can i avoid using dynamic from clause? Even if i don't know the database name, i prefer to use a static statement
SQL wont accept columnnames,tablenames,databasenames as parameters.so unless you you avoid them,you cant avoid dynamic sql..
Change your query to dynamic sql to avoid error..But again you will have a problem with temp tables scope
--This will fail ,because temp table falls under different scope
Declare #sql nvarchar(4000)
set #sql='
select *
into #tempTable
from #DBName.Invoices
where InvoiceId = 5'
---one option is to use global temp tables
declare #dbname varchar(1000)
set #dbname=db_name()
declare #sql nvarchar(4000)
set #sql='select *
into ##tempTable
from '+#DBName+'.dbo.test_Delete '
exec(#sql)
select * from ##temptable
But be carefull with above approach,since above temp table have global scope..
You also can use Openrowset ,some thing like below
select * into #temp from openrowset
('SQLNCLI','Server=yourinstancename;Trusted_Connection=yes;', 'select * form table')

SQL Command Name is Database Name; Use Dynamic SQL?

I have a database with the name "Union". I am trying execute SQL for this database in the MAINT table but since 'union' is a SQL command it is throwing errors. I can get the query to run when executing from Union database. Would dynamic SQL be able to fix my problem or should I change the database name?
I keep getting incorrect syntax near keyword 'UNION' here is what I have so far,
DECLARE #sql varchar(max)
DECLARE #Database varchar(5)
Set #Database = 'UNION'
SELECT #sql = 'SELECT '+#Database+' as ''Database'', '+#Database+'.hsi.useraccount.username as ''User Name'',
'+#Database+'.hsi.useraccount.realname as ''Real Name''
FROM '+#Database+'.hsi.useraccount
WHERE '+#Database+'.hsi.useraccount.username NOT LIKE ''%deactivated%'' and '+#Database+'.hsi.useraccount.username not like ''%administrator'' and '+#Database+'.hsi.useraccount.username not like ''%internal%'''
execute(#sql)
Add [] brackets around Schema names.
SELECT #sql = REPLACE('SELECT [#Database] as ''Database'', [#Database].hsi.useraccount.username as ''User Name'',
[#Database].hsi.useraccount.realname as ''Real Name''
FROM [#Database].hsi.useraccount
WHERE [#Database].hsi.useraccount.username NOT LIKE ''%deactivated%'' and [#Database].hsi.useraccount.username not like ''%administrator'' and [#Database].hsi.useraccount.username not like ''%internal%'''
,'#Database',#Database)
As long as the text "#Database" text doesn't appear anywhere else in your select statement, just throw it into a REPLACE() function and avoid all that embedded quote syntax and string concatenation headache.
You also can use quotename instead of manually entering square brackets
declare #db nvarchar(100)
set #db='performance'
declare #sql nvarchar(max)
set #sql='select * from '+QUOTENAME(#db)+'.'+quotename('dbo')+'.'+QUOTENAME('orders')
print #sql
exec(#sql)

Concatenated SQL statement

I'm working on a query that builds a list of table names from the sys.database master table. I then use those name in a concat statement to pull a specific piece if information out of each database. I'm running the whole thing through a while loop to hit all tables, with a counter.
i.e.
set #sql = 'select top 10 * from ' + (select dbname from #table where tabid = #i + '.dbo.**tablename** where NAME = '**String Value**'
When I just print the results of #sql, it gives me the correct syntax, and I am able to run the command with no problem.
When I set #sql to exec, I get an error
"database select top 10 * from dbname does not exist."
It's like the execute is ignoring everything after the select.
I'm suspecting you're calling EXEC incorrectly.
When calling 'exec' with a T-SQL query, be sure to enclose the target string in parenthesis, eg
DECLARE #FOO VARCHAR(100)
Set #FOO = 'SELECT TOP 5 * from SOMETABLE'
EXEC (#FOO) -- not EXEC #FOO

How to set a variable to the result of a sql query with a variable as a table name in SQL 2005

I'm currently having trouble writing a stored procedure and setting the value of a variable of type int to the results of a select statement with a variable as the tablename. I've looked at old threads and tried multiple methods, but no luck. If I'm not getting an error regarding the tablename, I end up getting an error with a variable conversion issue. I've been working on this for too long and any help would be appreciated. Below is a portion of my code. Thanks
DECLARE #BATCHNUMBER VARCHAR --value set in earlier code
DECLARE #ETABLE VARCHAR(50); --the table name
DECLARE #FIRSTDOCID INT;
SET #ETABLE = 'tablename_' + #BATCHNUMBER; --CREATE FIRST TABLE NAME
SELECT #FIRSTDOCID = MIN(D0CID) FROM #ETABLE
The error I get is: Must declare the table variable "#ETABLE"
You are trying to select from a VARCHAR, not a table. The only way to make this work is by using Dynamic SQL.
DECLARE #SQL NVARCHAR(250);
SET #SQL = 'SELECT #OUTPUT = MIN(D0CID) FROM ' + QuoteName(#ETABLE);
EXEC sp_executeSql #SQL, N'#output INT OUTPUT', #FIRSTDOCID OUTPUT;
SELECT #FIRSTDOCID;
However, I would not suggest using Dynamic SQL as this often leads to SQL injection.
You'll probably have to do something like use exec if you're dynamically building the query:
SET #QUERY = "SELECT" + ...etc.
exec(#QUERY)
Since ETABLE is a varchar, and not, as expected, a 'table variable'.

Must declare the scalar variable

#RowFrom int
#RowTo int
are both Global Input Params for the Stored Procedure, and since I am compiling the SQL query inside the Stored Procedure with T-SQL then using Exec(#sqlstatement) at the end of the stored procedure to show the result, it gives me this error when I try to use the #RowFrom or #RowTo inside the #sqlstatement variable that is executed.. it works fine otherwise.. please help.
"Must declare the scalar variable "#RowFrom"."
Also, I tried including the following in the #sqlstatement variable:
'Declare #Rt int'
'SET #Rt = ' + #RowTo
but #RowTo still doesn't pass its value to #Rt and generates an error.
You can't concatenate an int to a string. Instead of:
SET #sql = N'DECLARE #Rt int; SET #Rt = ' + #RowTo;
You need:
SET #sql = N'DECLARE #Rt int; SET #Rt = ' + CONVERT(VARCHAR(12), #RowTo);
To help illustrate what's happening here. Let's say #RowTo = 5.
DECLARE #RowTo int;
SET #RowTo = 5;
DECLARE #sql nvarchar(max);
SET #sql = N'SELECT ' + CONVERT(varchar(12), #RowTo) + ' * 5';
EXEC sys.sp_executesql #sql;
In order to build that into a string (even if ultimately it will be a number), I need to convert it. But as you can see, the number is still treated as a number when it's executed. The answer is 25, right?
In your case you can use proper parameterization rather than use concatenation which, if you get into that habit, you will expose yourself to SQL injection at some point (see this and this:
SET #sql = #sql + ' WHERE RowNum BETWEEN #RowFrom AND #RowTo;';
EXEC sys.sp_executesql #sql,
N'#RowFrom int, #RowTo int',
#RowFrom, #RowTo;
You can also get this error message if a variable is declared before a GOand referenced after it.
See this question and this workaround.
Just FYI, I know this is an old post, but depending on the database COLLATION settings you can get this error on a statement like this,
SET #sql = #Sql + ' WHERE RowNum BETWEEN #RowFrom AND #RowTo;';
if for example you typo the S in the
SET #sql = #***S***ql
sorry to spin off the answers already posted here, but this is an actual instance of the error reported.
Note also that the error will not display the capital S in the message, I am not sure why, but I think it is because the
Set #sql =
is on the left of the equal sign.
Sometimes, if you have a 'GO' statement written after the usage of the variable, and if you try to use it after that, it throws such error. Try removing 'GO' statement if you have any.
This is most likely not an answer to the issue itself, but this question pops up as first result when searching for Sql declare scalar variable hence I want to share a possible solution to this error.
In my case this error was caused by the use of ; after a SQL statement. Just remove it and the error will be gone.
I guess the cause is the same as #IronSean already posted in a comment above:
it's worth noting that using GO (or in this case ;) causes a new branch where declared variables aren't visible past the statement.
For example:
DECLARE #id int
SET #id = 78
SELECT * FROM MyTable WHERE Id = #var; <-- remove this character to avoid the error message
SELECT * FROM AnotherTable WHERE MyTableId = #var
Just adding what fixed it for me, where misspelling is the suspect as per this MSDN blog...
When splitting SQL strings over multiple lines, check that that you are comma separating your SQL string from your parameters (and not trying to concatenate them!) and not missing any spaces at the end of each split line. Not rocket science but hope I save someone a headache.
For example:
db.TableName.SqlQuery(
"SELECT Id, Timestamp, User " +
"FROM dbo.TableName " +
"WHERE Timestamp >= #from " +
"AND Timestamp <= #till;" + [USE COMMA NOT CONCATENATE!]
new SqlParameter("from", from),
new SqlParameter("till", till)),
.ToListAsync()
.Result;
Case Sensitivity will cause this problem, too.
#MyVariable and #myvariable are the same variables in SQL Server Man. Studio and will work. However, these variables will result in a "Must declare the scalar variable "#MyVariable" in Visual Studio (C#) due to case-sensitivity differences.
Just an answer for future me (maybe it helps someone else too!). If you try to run something like this in the query editor:
USE [Dbo]
GO
DECLARE #RC int
EXECUTE #RC = [dbo].[SomeStoredProcedure]
2018
,0
,'arg3'
GO
SELECT month, SUM(weight) AS weight, SUM(amount) AS amount
FROM SomeTable AS e
WHERE year = #year AND type = 'M'
And you get the error:
Must declare the scalar variable "#year"
That's because you are trying to run a bunch of code that includes BOTH the stored procedure execution AND the query below it (!). Just highlight the one you want to run or delete/comment out the one you are not interested in.
If someone else comes across this question while no solution here made my sql file working, here's what my mistake was:
I have been exporting the contents of my database via the 'Generate Script' command of Microsofts' Server Management Studio and then doing some operations afterwards while inserting the generated data in another instance.
Due to the generated export, there have been a bunch of "GO" statements in the sql file.
What I didn't know was that variables declared at the top of a file aren't accessible as far as a GO statement is executed. Therefore I had to remove the GO statements in my sql file and the error "Must declare the scalar variable xy" was gone!
As stated in https://learn.microsoft.com/en-us/sql/t-sql/language-elements/sql-server-utilities-statements-go?view=sql-server-ver16 , the scope of a user-defined variable is batch dependent .
--This will produce the error
GO
DECLARE #MyVariable int;
SET #MyVariable = 1;
GO --new batch of code
SELECT #MyVariable--CAST(#MyVariable AS
int);
GO
--This will not produce the error
GO
DECLARE #MyVariable int;
SET #MyVariable = 1;
SELECT #MyVariable--CAST(#MyVariable AS int);
GO
We get the same error when we try to pass a variable inside a dynamic SQL:
GO
DECLARE #ColumnName VARCHAR(100),
#SQL NVARCHAR(MAX);
SET #ColumnName = 'FirstName';
EXECUTE ('SELECT [Title],#ColumnName FROM Person.Person');
GO
--In the case above #ColumnName is nowhere to be found, therefore we can either do:
EXECUTE ('SELECT [Title],' +#ColumnName+ ' FROM Person.Person');
or
GO
DECLARE #ColumnName VARCHAR(100),
#SQL NVARCHAR(MAX);
SET #ColumnName = 'FirstName';
SET #SQL = 'SELECT ' + #ColumnName + ' FROM Person.Person';
EXEC sys.sp_executesql #SQL
GO
Give a 'GO' after the end statement and select all the statements then execute