Fully qualified table names with SP_ExecuteSql to access remote server - sql

Trying to update a table on a linked server (SQL 2000/2005) but my server name will not be known ahead of time. I'm trying this:
DECLARE #Sql NVARCHAR(4000)
DECLARE #ParamDef NVARCHAR(4000)
DECLARE #SERVER_NAME VARCHAR(35)
SET #Sql = 'UPDATE
#server_name_param.dba_sandbox.dbo.SomeTable
SET SomeCol=''data'''
SET #ParamDef = N'#server_name_param VARCHAR(35)'
print #Sql
exec sp_executesql #Sql, #ParamDef, #server_name_param=#SERVER_NAME
Which returns this:
UPDATE
#server_name_param.dba_sandbox.dbo.SomeTable
SET SomeCol='data'
Msg 170, Level 15, State 1, Line 2
Line 2: Incorrect syntax near '.'.
Any ideas? Is there anyway I view the SQL statement that is being executed after the parameters are bound?

You'll have to do this, it can't be parameterised
....
SET #Sql = 'UPDATE ' + #server_name_param + '.dba_sandbox.dbo.SomeTable SET SomeCol=''data'''
....
Edit: There is another way which I used back in my pure DBA days
EXEC sp_setnetname 'AdhocServer', #SERVER_NAME
UPDATE AdhocServer.dba_sandbox.dbo.SomeTable SET SomeCol 'data'
EXEC sp_setnetname 'AdhocServer', 'MeaninglessValue'
sp_setnetname is there from SQL Server 2000 to 2008
Edit2. Permissions:
Try EXECUTE AS LOGIN = 'login_name' , where login_name is a superuser
I've not really used this (I use "AS USER" for testing), so not sure of the finer points...
Edit 3: for concurrency, consider using sp_getapplock and a stored procedure, or some other concurrency control mechanism.

You cannot do this with parameters directly - you would have to use dynamic SQL, or send the server name as a parameter to an SP that does dynamic SQL:
DECLARE #template NVARCHAR(4000)
DECLARE #Sql NVARCHAR(4000)
DECLARE #SERVER_NAME VARCHAR(35)
SET #template = 'UPDATE {#server_name_param}.dba_sandbox.dbo.SomeTable SET SomeCol=''data'''
SET #sql = REPLACE(#template, '{#server_name_param}', #SERVER_NAME)
print #Sql
exec sp_executesql #Sql -- OR EXEC ( #sql )

I like gbn's trick. I didn't know that one and I'm gonna have to research that some more.
Since I didn't know that trick, I've had to use dynamic sql in similar situations in the past (like what Cade posted). When that happens I would normally query an information schema view to make sure the parameter value is a real database object before building the query. That way I'm sure it's not an injection attempt.

Related

Scalar variable must be declared in SQL variable

I'm creating a report using sql scripts through management studio and I'm getting the error " Must Declare the scalar variable "#Account". I've been reading other similar questions on this portal but they are related to c#
I'm currently trying to reduce the code on the script so I decided to put a sql script into a variable because depending on a condition the where condition will change. Below is an example of the code
Declare #Account int = 1 , #SQL varchar(max)=''
Select #SQL = N'Select ColumnA,ColumnB, ColumnC from Table1 where ColumnA =1'
if #Account IS NULL
Begin
exec(#SQL)
end
--Here is where the error is hapening
else
begin
--This is the line causing the error
Select #SQL = #SQL + 'AND ColumnB=#Account"
exec(#SQL)
end
If I type manually the value of the variable next to "ColumnB=" it works but the account number will be selected by the user executing the script. I'm thinking on maybe building a temp table to capture the variable value and then do a sub query on the where condition but maybe the solution to this error may be more easier
You want sp_executesql:
select #SQL = #SQL + 'AND ColumnB=#Account';
exec sp_executesql #SQL, N'#Account int', #Account=#Account;
This is how you pass parameters into a dynamic SQL statement in SQL Server. I strongly recommend that you only use sp_executesql to execute SQL statements -- even when you don't have parameters. Using it makes it easy to implement parameters when you need them.
You are passing in '#Account' into the #SQL variable -- the underlying EXEC cannot see that variable.
One way of fixing this would instead be to do this:
Select #SQL = #SQL + 'AND ColumnB=' + CAST(#Account as varchar)

Get error in string query

I'm a beginner to SQL Server
I wrote this query:
DECLARE #sql nvarchar(1000) = 'UPDATE Work
SET [Name] = Programmer, [ImageAddress] = pic.jpg
WHERE Id = 2'
SELECT #sql
EXEC Sp_executesql #sql
but I get this error
Invalid column name 'Programmer'.
Why do I get this error?
Thank you for your help
You are dealing with SQL in strings. Quoting the strings becomes a challenge. You need for Programmer to be in single quotes when the query is executed. To get this, you need double single quotes in the string:
DECLARE #sql nvarchar(1000)='
UPDATE Work
SET [Name] = ''Programmer'', [ImageAddress] = ''pic.jpg'' WHERE Id=2'
select #sql
EXEC Sp_executesql #sql;
Because you are wise enough to use sp_executesql, you should learn about parameters. You can write the query as:
DECLARE #sql nvarchar(1000)='
UPDATE Work
SET [Name] = #Programmer, [ImageAddress] = #imageaddress WHERE Id=2'
select #sql
EXEC Sp_executesql #sql, N'#programmer nvarchar(255), #imageaddress nvarchar(255)',
#programmer = N'Programmer', #imageaddress = N'pic.jpg';
This has several advantages besides the quoting. It is safer in terms of SQL injection and it allows SQL Server to cache the execution plans if the query is called more than once.
try this:
You need to use '' (Double Quotes for string) Inside Dynamic SQL
DECLARE #sql nvarchar(1000)='
UPDATE Work
SET [Name] = ''Programmer'',[ImageAddress] =''pic.jpg'' WHERE Id=2'
select #sql
EXEC Sp_executesql #sql

SQL Server dynamic query -- cannot locate linked server

declare #node int = 9044;
DECLARE #sqlCommand NVARCHAR(MAX) =
(
'SELECT * FROM [#node].[database_name].dbo.table_name'
);
DECLARE #paramList NVARCHAR(400) =
(
'#node int'
)
exec sp_executesql #sqlCommand, #paramlist, #node;
So this is the simple query I'm attempting to run. 9044 is fine. Running that query normally works perfectly (obviously I've removed the db and table names). Not entirely sure what is wrong with it. The error I get is:
Msg 7202, Level 11, State 2, Line 1
Could not find server '#node' in sys.servers. Verify that the correct server name was specified. If necessary, execute the stored procedure sp_addlinkedserver to add the server to sys.servers.
Any ideas on how to fix this issue or should I just write the query and use EXEC (#sql)
As per my thinking and test it only allow parameter in query part like in where and other condition.
Try this way.
declare #node int = 9044;
DECLARE #sqlCommand NVARCHAR(MAX) =
(
'SELECT * FROM [#node].[database_name].dbo.table_name'
);
DECLARE #paramList NVARCHAR(400) =
(
'#node int'
)
SET #sqlCommand = REPLACE(#sqlCommand , '#node',#node)
exec sp_executesql #sqlCommand, #paramlist, #node;
You are using 3-dot notation which defines the server.db.table #nodes is looking for a server of this name ...are you looking for this server name dynamically.. the best way would be to create a linked server object or alias and refer to it this was i.e
MyServer = dev-sql-server.AdventureWorks etc
Or you may just need to get rid of the extra [#node].

How to use a varying database?

I want to use a database which name is stored in a variable. How do I do this?
I first thought this would work but it doesn't:
exec('use '+#db)
That will not change database context
Suggestions anyone?
Unfortunately I don't know of a direct solution to this one. The nearest working version is:
DECLARE #db nvarchar(MAX)
SET #db = 'use DBname'
Exec sp_executesql #db
but this only changes the context for the length of the procedure call. However, more statements can be included in that call to make use of the context:
DECLARE #sql nvarchar(MAX)
SET #sql = 'use DBName SELECT * FROM Table1'
Exec sp_executesql #sql
If you absolutely have to do this using dynamic SQl, I prefer this:
DECLARE #sql nvarchar(MAX)
declare #databasename varchar (20)
Set #databasename = mydatabase
SET #sql = 'SELECT * FROM ' + #databasename + 'dbo.Table1'
Exec sp_executesql #sql
The reason I prefer it is that you can extend it to use multipe datbases in the same query if need be.
I havea a concern that you don't know the datbase name for each table already without resorting to dynamic means. In other words, why can't you write:
SELECT * FROM mydatabase.dbo.Table1
If you have multiple databases with the same table names, likely you have a design problem.
The use statement is only in scope inside the exec block. Therefore you would have to do everything else in the same exec:
exec('use '+ #db + '
--do other stuff'
)
Presumably you know all the possible database names. One (slightly inelligant) way of doing this would be to use a CASE or multiple IF statements to test the variable and hardcode the USE statement for each case.

SQL with table name as parameter and query longer than 4000 characters

I am trying to write a stored procedure that takes a table name as a parameter. Yes I already know this is a security vulnerability, but this is an internal stored proc that doesn't face typical risks of SQL Injection.
What I have so far is something like the following:
CREATE PROCEDURE [dbo].[myprocedure]
#tableName sysname
AS
DECLARE #cmd nvarchar(4000)
SET #cmd = N' Select blah blah from ' + #tableName
EXEC (#cmd)
GO
The query will work in theory, but my problem is that my query is longer than 4000 characters. Is there another way to use #tableName in a cmd variable longer than 4000 characters (which is nvarchar's max)?
If you use SQL Server >= 2005, try replacing nvarchar(4000) with nvarchar(MAX).
DECLARE #cmd NVARCHAR(MAX);
Extract some of your logic into views or user defined functions.
Use
DECLARE #cmd VARCHAR(8000)
instead of DECLARE #cmd NVARCHAR(MAX);
NVARCHAR(MAX) ALLOWS ONLY 4000 CHARACTERS.