SQL simple query on multiple linked servers & databases - sql

I have this simple SQL query in SSMS select * from dbo.table1 where sucode = 'a002'
I am trying to learn how to run this on all linked servers on databases ending in %live
I've never done this before and what I have seen online hasn't really helped.
I know I need to have QUOTENAME(d.name, '''') ....WHERE NAME like '%live' for the database name and possibly the same for the server? Or maybe an OPENQUERY for the server? Or a link?
If anyone has a template they use or any advise that would be hugely appreciated.

You can use undocumented stored procedure MS_ForeachDb to execute the query against each database. You can use sys.servers to get the list of linked servers and run them against each linked server.
Read MS_ForeachDB
Read about sys.servers
SELECT 'SELECT * FROM OPENQUERY(' + name + ',''EXECUTE master.sys.sp_MSforeachdb ''USE [?];select * from dbo.table1 where sucode = ''a002'''')'
FROM sys.servers WHERE is_linked = 1; -- avoid pulling from local server
Note: Using undocumented procedures is not recommended. Read more on this using undocumented procedures bad practice

Related

Run complex update query on remote server using OPENROWSET

Could you please help me with below task. I need to run below query on remote server
UPDATE prod
SET prod.count = ( SELECT SUM(Inv) FROM cost WHERE pID = prod.ID)
WHERE (( SELECT COUNT(id) FROM Cost WHERE pID = prod.ID ) > 0)
I have tried to use OPENROWSET but do not have enough experience working with it and all online examples with OPENROWSET that I saw use only one table. Can you please give me an idea how to modify this query to use OPENROWSET or ideas of any other solutions?
You can use direct linked server or OPENQUERY (linked_server_name ,'your query').
Best way to to this is to create procedure on target instance and use only easy exec on linked server to run procedure.
exec LinkedServer.TargetDB.TargerSc.NewProcedure
Openrowset is used to "open row set" - primary to access remote data, to modify remote data is openquery.

Accessing multiple SQL databases with the same schema

I am creating a new SQL database that needs to connect to several existing DB's, all of which have the same schema.
I would like to create stored procedures that can be used to access any of the existing DB's whilst re-using code as much as possible. My question is, what is the best way to write generic SP's that can be used to access any one of these existing databases?
Here are some options I have considered so far. Please note, these are just simple code snippets to illustrate the question, not real-world code. First I tried switching on the DB name:
IF #db = 'A'
BEGIN
SELECT * FROM A.dbo.SERIES_DATA
END
IF #db = 'B'
BEGIN
SELECT * FROM B.dbo.SERIES_DATA
END
This has the disadvantage that the same SQL statement is repeated several times. So then I thought of using dynamic SQL:
DECLARE #Command varchar(100)
SET #Command = 'select * from ' + #db + '.dbo.SERIES_DATA'
EXEC (#Command)
This solves the problem of duplicate staements, but has the risks of dynamic SQL (e.g. injection attacks etc). So finally I hit on the idea of creating a Table-valued Function:
CREATE FUNCTION [dbo].[ufnSeries_Data] (#db varchar(10))
RETURNS TABLE
AS
RETURN (
SELECT * FROM A.dbo.Series_Data WHERE #db = 'A'
UNION
SELECT * FROM B.dbo.Series_Data WHERE #db = 'B'
)
GO
This gives me the ability to write single, generic code to access any of the DB's:
SELECT * FROM ufnSeries_Data(#db)
The problem with this approach is that I have to give all users of the system read-access to all of the databases in order for this query to work, otherwise I get an access-denied error. I'm also not certain of the performance impact of creating a TVF like this.
Does anyone has any other ideas for how I can structure my code to minimize duplication of statements? Maybe there is a much simpler approach that I'm missing? I would be very grateful for any suggestions.
You can do this by removing the reference to your database and schema
SELECT * FROM SERIES_DATA
It is a question of creating the schema in all your databases, and granting the login access to the databases and schemas (in this case you are using the default dbo schema).
If you are using the management studio, when you create logins, you can handle the rest with user mapping.
If you have the same schema (e.g. dbo), you can change your select to:
SELECT * FROM dbo.SERIES_DATA
You really don't want to include the database name unless you are joining tables across databases.

Encrypted query in sql server

I want to execute a query in our client's server using remove access. If I execute a query something like this Update abc set col1=12 where id = 2 they will understand what we are doing. So I want some method for encryption and decryption like this:
In our server I encrypt a query like this:
encrypt(Update abc set col1=12 where id = 2)
So I get output like:
0x0100CF465B7B12625EF019E157120D58DD46569AC7BF4118455D12625EF019E157120D58DD46569AC7BF4118455D
And I execute a this encrypted query on client's machine like this:
decrypt(0x0100CF465B7B12625EF019E157120D58DD46569AC7BF4118455D12625EF019E157120D58DD46569AC7BF4118455D)
So our client can't understand what we have executed.
you can create a simple decrypt function like this..
create proc exec_decrypt(#sql_str varbinary(8000))
as
begin
declare #qry varchar(8000);
select #qry=cast(#sql_str as varchar(8000));
exec(#qry);
end
which accepts a varbinary string and converts to varchar and then execute it..
You can generate the encrypted query by using the satament below
select CAST('UPDATE users set name =''alex''' as varbinary(8000))
then execute the proc exec_decrypt in client place by passing the output of the above query as the parameter for the procedure..
Ex: exec_decrypt 0x55504441544520757365727320736574206E616D65203D27616C657827
Hope this will work for you.. Please note that client should not have any permission on the
proc exec_decrypt
The only way I have found to ensure that no one can view your queries is to put them in a function or procedure using 'with encryption'. Run the below code to see for yourself. Dynamic SQL suggested in the earlier answer doesn't fully solve your problem.
create procedure dbo.dummy_drop_me with encryption as select t.* from sys.dm_exec_requests r cross apply sys.dm_exec_sql_text (r.sql_handle) t where r.session_id = ##spid
go
exec dbo.dummy_drop_me
go
select t.* from sys.dm_exec_requests r cross apply sys.dm_exec_sql_text (r.sql_handle) t where r.session_id = ##spid
go
exec ('select t.* from sys.dm_exec_requests r cross apply sys.dm_exec_sql_text (r.sql_handle) t where r.session_id = ##spid')
The only way I have found to keep someone from capturing your functions and procedures is to create them in their own database on a server where no one has access. Then, attach a copy of the database to the servers where you need them.
In my opinion, With Encryption is generally counter-productive as there are a number of Sql Server decryption tools. ie dbForge's DB Decryptor. I just used this to decrypt a database's T-SQL, so I could export DACPAC's and BACPAC's for use in Azure DevOps automated deployments. With Encryption causes DACPAC and BACPAC exports to fail. This means, if you implement With Encryption you cannot use DACPAC to create update scripts for DB Patches or direct updates, you cannot use Visual Studio Sql Server Tools projects, which makes DB development harder. You have not secured anything, just made it harder for Dev's and Op's to do their jobs.

How to determine if database exists on linked server?

I know you can do something like:
select count(*) as Qty from sys.databases where name like '%mydatabase%'
but how could you do something like:
select count(*) as Qty from linkedServer.sys.databases where name like '%mydatabases%'
I guess I could put a stored procedure on the linked server and execute the first select, but is there a way to query a linked server for what databases it holds?
Assuming your linked server login has read permissions on the master.sys.databases table, you can use the following:
select * from linkedserver.master.sys.databases
In the past, I've used this very query on SQL Server 2008 R2.
I think its just a matter of your syntax that is stopping you, try using single quotes instead of %% around your database name:
SELECT COUNT(*) as Qty FROM LinkedServer.master.sys.databases where name like 'mydatabase'
The correct formatting for selecting a Linked Server has already been answered here:
SQL Server Linked Server Example Query
Listed below is a link to a cursor that works:
http://jasonbrimhall.info/2012/03/05/are-my-linked-servers-being-used/
The query will need some rework to include all functions and triggers though.
I'm not sure if a remote master DB is always available through a linked server.
I'll be using the following TRY CATCH probe
BEGIN TRY
EXEC ('SELECT TOP 1 1 FROM MyLinkedServer.MyTestDb.INFORMATION_SCHEMA.TABLES')
END TRY
BEGIN CATCH
PRINT 'No MyTestDB on MyLinkedServer'
END CATCH

SQL - How to insert results of Stored_Proc into a new table without specifying columns of new table?

Using SQL Server 2005, I'd like to run a stored procedure and insert all of the results into a new table.
I'd like the new table to have its columns automatically configured based upon the data returned by the stored procedure.
I am familiar with using the SELECT ... INTO syntax:
SELECT * INTO newtable FROM oldtable
Is this possible?
Edit for clarification: I'm hoping to accomplish something like:
Select * INTO newtable FROM exec My_SP
The only way to do this is w/ OPENROWSET against the local server:
SELECT * INTO #temp
FROM OPENROWSET (
'SQLOLEDB'
, 'Server=(local);TRUSTED_CONNECTION=YES;'
, 'SET FMTONLY OFF EXEC database.schema.procname'
) a
But this is kind of a last-ditch-gotta-do-it-damn-the-consequences kind of method. It requires elevated permissions, won't work for all procedures, and is generally inefficient.
More info and some alternatives here: http://www.sommarskog.se/share_data.html
This seems like a horrible design. You're really going to create a new table to store the results of a stored procedure, every time the stored procedure is called? And you really can't create the table in advance because you have absolutely no idea what kind of output the stored procedure has? What if the stored procedure returns multiple resultsets? What if it has side effects?
Okay, well, if that's what you really want to do...
One way to accomplish this is to use your local server as a linked server and utilize OPENQUERY. First you need to make sure your local server is configured for data access:
EXEC sp_serveroption 'local server name', 'DATA ACCESS', true;
Then you can do something like this:
SELECT * INTO dbo.newtable
FROM OPENQUERY('local server name', 'EXEC yourdb.dbo.yourproc;');
PS How are you going to write code that is going to perform SELECT INTO into a new table name every time (because you can only do SELECT INTO once)? Dynamic SQL? What happens if two users run this code at the same time? Does one of them win, and the other one just gets an error message?
A variation of the same is
create table somename
select * from wherever;