I want to be able to execute remote queries based on the results of a local query.
For instance:
DECLARE #REMOTESERVER VARCHAR(10)
Select TOP 1 #REMOTESERVER = RemoteServer from TABLE
--Execute the next query on a remote server from the value I retrieved above
Select * from tblCustomers
What RDBMS are you using? Some will not support a pure sql way of doing this. Others, like SQL Server, might support this scenario. Is the remote server accessible via a linked server that you can access. You could then use dynamic sql to create your sql string. Something like this should work in SQL Server:
SET #Sql = 'SELECT * FROM [' + #RemoteServer + '].dbname.schema.tblCustomers'
EXEC #Sql
Here is a post about linked servers: https://stackoverflow.com/a/4091984/1073631
Related
I have added a linked server in my sql server 2008. I want to fetch data from a table and a table valued function residing on my linked server and join this data on a local table by following below given naming convention.
<server>.<database>.<schema>.<table>
However my first problem is I need to fetch <server> part from a table. So when I try to do something like following it fails
Select * FROM #ServerNameVariable.database.dbo.myTable
Any idea how can I form fully qualified linked server table name with a user defined variable ?
My SP is as follows
CREATE PROCEDURE TEST_SP
AS
BEGIN
DECLARE #NetworkDBName VARCHAR(255)
SET #NetworkDBName = '[MyLinkedServerName]'
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
select * from #NetworkDBName + '.' + testDatabase.dbo.Invoice_tb
END
GO
You cannot use variables in place of database, schema or table names.
Instead you can build and execute dynamic SQL statements, using sp_ExecuteSQL.
This example won't work, as the server name is seen as a string and not a server object.
Failed Example
/* Anti-pattern.
* Does not work.
*/
DECLARE #Server SYSNAME = 'Server001';
SELECT
*
FROM
#Server.Database1.dbo.Table1
;
This example shows a method that does work. Here the SQL statement is built as a string, which is then executed.
/* Dynamic SQL statement.
* Will work.
*/
DECLARE #Server SYSNAME = 'Server001';
DECLARE #Statement NVARCHAR(255);
SET #Statement = 'SELECT * FROM ' + QUOTENAME(#Server) + '.Database1.dbo.Table1;';
EXECUTE sp_ExecuteSQL #Statement;
As ever; please be careful when generating and executing dynamic SQL statements. You do not want to open yourself up to SQL injection attacks. Look into OPENROWSET or check the passed server name against the code kindly supplied by #Devart above (SELECT name FROM sys.servers WHERE server_id > 0) before executing.
EDIT 1: Added more detail to the paragraph on SQL injection.
EDIT 2: Removed square brackets from 2nd example query, replaced with QUOTENAME, as per #TTs comment.
Use the name of link server it will a 4 part qualifier e.g
Select * From [Link Server Name ].[Database].[Schema].[Table]
I'm working on two different servers with the same structure, one of them is where we develope, and the other one is the live server. My main database (let's call it MainDB) has the same name in both databases, but the others (call those DBi in live server and DB_i in developement server) do not. I have some syncronization stored procedures which transfer data from the databases DBi to MainDB. In the developement server, I use different names then the live server, then after every change in the sync procedures of developement server, I have to change the names of the databases before transfering them to live server.
Now, my aim is to write functions which would return the names of the databases. However, I have no idea what type of data to return in order to use the return value as the name of the database.
For instance, assume I have a query like:
SELECT * FROM DB1.dbo.Table1
in live server. In the developement server, it's:
SELECT * FROM DB_1.dbo.Table1
And my aim is to be able to define something like:
DECLARE #DBName1 [SOME_TYPE]= GetDBName(#DBID_1)
and then use it as:
SELECT * FROM #DBName1.dbo.Table1
It doesn't have to be exactly like this of course, but the functionality must be the same. You may ask why would I need something like that. I don't wanna change the code before all the transfers. I wanna use the same sp in both servers, but get different names for databases from the functions, as I would only change the return value of the functions in servers for only one time.
CREATE PROCEDURE usp_SomeProc
#DB_Name NVARCHAR(128)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT * FROM ' + QUOTENAME(#DBName1) + N'.[dbo].[Table1] '
EXECUTE sp_executesql #Sql
END
Edit
DECLARE #Query NVARCHAR(MAX);
DECLARE #DBName nvarchar(50) = 'DBName'
SET #Query = N'SELECT * FROM ' + QUOTENAME(#DBName) + N'.dbo.Table'
EXECUTE sp_executesql #Query
EDIT
The final goal is to call a stored procedure hosted in sybase with input and output parameters from SQL Server 2008 via Linked Server
I think title is pretty clear.
My goal is to execute a stored procedure hosted in Sybase SQL Anywhere 8 in SQL Server 2008 through the linked server I already created.
Any SQL query made through the linked server is working.
In addition I was able to execute a function but I don't now how to get the return value like that
EXEC ('CALL "dbname"."procedurename"(''param1'', ''param2'', ''param3'')') AT LinkedServerAlias;
Thanks 4 all your help!
Mauro
can you use four part naming convention?
like
exec LinkedServerName.dbname.dbo.procedurename #param1, #param2, #param3
I was finally able to do it by calling
SELECT * FROM OPENQUERY([LinkedServer], 'SELECT "dbname"."spname"(#p1,#p2, #p3)')
I'll add comments and example as soon as I experiment it.
4 part object names are valid only for SQL Server linked servers.
You have to have your EXEC inside an OPENQUERY
SELECT * FROM OPENQUERY([LinkedServer], 'EXEC MyDB.MyScheme.MyProc.spname #p1, #p2, #p3')
Now, you can't parametrise OPENQUERY calls so you have use dynamic SQL
DECLARE #sql nvarchar(4000), #linkedsql nvarchar(4000)
SET #sql = 'EXEC MyDB.MyScheme.MyProc.spname ' + CAST(#p1value as int) + ...
SET #linkedsql = 'SELECT * FROM OPENQUERY(LinkedServer, ''' + #sql + ''')'
EXEC (#linkedsql)
On my SQL 2005 server, I have a linked server connecting to Oracle via the OraOLEDB.Oracle provider.
If I run a query through the 4 part identifier like so:
SELECT * FROM [SERVER]...[TABLE] WHERE COLUMN = 12345
It takes over a minute to complete. If I run the same query like so:
SELECT * FROM OPENQUERY(SERVER, 'SELECT * FROM TABLE WHERE COLUMN = 12345')
It completes instantly. Is there a setting I'm missing somewhere to get the first query to run in a decent period of time? Or am I stuck using openquery?
In your first example using "dot" notation, client cursor engine is used and most things are evaluated locally. If you're selecting from a large table and using a WHERE clause, the records will be pulled down locally from the remote db. Once the data has been pulled across the linked server, only then is the WHERE clause is applied locally. Often this sequence is a performance hit. Indexes on the remote db are basically rendered useless.
Alternately when you use OPENQUERY, SQL Server sends the sql statement to the target database for processing. During processing any indexes on the tables are leveraged. Also the where clause is applied on the Oracle side before sending the resultset back to SQL Server.
In my experience, except for the simplest of queries, OPENQUERY is going to give you better performance.
I would recommend using OpenQuery for everything for the above reasons.
One of the pain points when using OpenQuery that you may have already encountered is single quotes. If the sql string being sent to the remote db requires single quotes around a string or date date they need to be escaped. Otherwise they inadvertantly terminate the sql string.
Here is a template that I use whenever I'm dealing with variables in an openquery statement to a linked server to take care of the single quote problem:
DECLARE #UniqueId int
, #sql varchar(500)
, #linkedserver varchar(30)
, #statement varchar(600)
SET #UniqueId = 2
SET #linkedserver = 'LINKSERV'
SET #sql = 'SELECT DummyFunction(''''' + CAST(#UniqueId AS VARCHAR(10))+ ''''') FROM DUAL'
SET #statement = 'SELECT * FROM OPENQUERY(' + #linkedserver + ', '
SET #Statement = #Statement + '''' + #SQL + ''')'
EXEC(#Statement)
Is it possible to construct a dynamic query to for a linked server (and if so how)?
For example:
#linkedServer varchar(50)
#var1 varchar(10)
#var2 varchar(10)
select *
from openquery(#linkedServer,
'select c1,c2
from t1
where p1 = #var1
and p2= #var2')
example
exec ('select * from openquery(' + #linkedServer +
', ''select c1,c2 from t1 where p1 = '' + #var1 + ''and p2= '' + #var2 + ''')
make sure to read The Curse and Blessings of Dynamic SQL to protect against SQL injection
see EXEC() at Linked Server section of The Curse and Blessings of Dynamic SQL by Erland Sommarskog
from that article:
A special feature added in SQL 2005 is
that you can use EXEC() to run
pass-through queries on a linked
server. This could be another instance
of SQL Server, but it could also be an
Oracle server, an Access database,
Active directory or whatever. The SQL
could be a single query or a sequence
of statements, and could it be
composed dynamically or be entirely
static. The syntax is simple, as seen
by this example:
EXEC('SELECT COUNT(*) FROM ' + #db +
'.dbo.sysobjects') AT SQL2K
SQL2K is here a linked server that has
been defined with sp_addlinkedserver.