Making dynamic queries for linked server in SQL Server - sql-server

I want to create a dynamic query in SQL Server which will run on linked server. I am trying to do it as follows.
USE [MYDB]
GO
DECLARE #company AS nvarchar(50);
DECLARE #id nvarchar(MAX);
DECLARE #query nvarchar(MAX);
SET #company = 'mycompany.com';
SET #query = N'SELECT #csid = id FROM OPENQUERY(LINKSERVER12,
''SELECT id from company where name = #comp'')';
EXECUTE sp_executesql #company_query, N'#comp nvarchar(50), #csid
nvarchar(MAX) OUTPUT', #comp = #company,#csid = #id OUTPUT
In the above script, I want to pass the value for #comp dynamically. For that I tried setting input and output variable while executing SQL with sp_executesql.
I am getting the following error
Syntax error in SQL statement. Syntax error line 1 at or after token .[10179].
Msg 7321, Level 16, State 2, Line 4
An error occurred while preparing the query "SELECT id from company where name = #comp" for execution against OLE DB provider "MSDASQL" for linked server "LINKSERVER12".
The error is happening at the dynamic query
N'SELECT #csid = id FROM OPENQUERY(LINKSERVER12,
''SELECT id from company where name = #comp'')'
I tried replacing #comp in the SQL query with ''#comp'', ''''#comp'''' with no luck. Any help is greatly appreciated.

Just build the string query wihtout parameters.
USE [companyDB]
GO
DECLARE
#companyName AS nvarchar(50)
,#id nvarchar(MAX)
,#query NVARCHAR(MAX)
SET #companyName = 'AMAZON'
DECLARE #idTable TABLE
(
id INT
)
--Repace Server, UID and PWD
SET #query =
N'SELECT
[id]
FROM OPENROWSET
(
N''SQLNCLI''
,N''Server=10.111.1.111;UID=username;PWD=password123;''
,N''SELECT [id]
FROM [companyDB]
WHERE [name] = '''''+#companyName+'''''''
)'
INSERT INTO #idTable
EXECUTE (#query)
SELECT TOP 1
#id = id
FROM #idTable

Related

Get data from many databases - dynamic database

We are using SQL Server 2014 Enterprise with many databases. I have to execute query and get reports / data from every database with EXACT SAME Schema and database starts with Cab
When a new company is added in our ERP project a new database is created with exact schema starting with Cab and incremented number is assigned to it like:
Cab1
Cab2
Cab3
Cab5
Cab10
I can get the database names as:
SELECT name
FROM master.sys.databases
where [name] like 'Cab%' order by [name]
I have to create a Stored Procedure to get data from tables of every database.
How to do that using a Stored Procedure as the databases are created dynamically starting with Cab?
You can use EXEC(#Statement) or EXEC SP_EXECUTESQL if you have to pass parameters.
CREATE OR ALTER PROCEDURE dbo.GetDataFromAllDatabases
AS
BEGIN
DECLARE #T TABLE (id INT NOT NULL IDENTITY(1, 1), dbName VARCHAR(256) NOT NULL)
INSERT INTO #T
SELECT NAME FROM MASTER.SYS.DATABASES WHERE [NAME] LIKE 'Cab%' ORDER BY [NAME]
CREATE TABLE #AllData (......)
DECLARE #Id INT, #DbName VARCHAR(128)
SELECT #Id = MIN(Id) FROM #T
WHILE #Id IS NOT NULL
BEGIN
SELECT #DbName = dbName FROM #T WHERE Id = #Id
DECLARE #Statement NVARCHAR(MAX)
SET #Statement = CONCAT(N'INSERT INTO #AllData (...) SELECT .... FROM ', #DbName, '.dbo.[TableName]')
EXEC(#Statement);
--YOU CAN USE BELOW LINE TOO IF YOU NEED TO PASS VARIABLE
--EXEC SP_EXECUTESQL #Statement, '#Value INT', #Value = 128
SET #Id = (SELECT MIN(Id) FROM #T WHERE Id > #Id)
END
END
A quick and easy dynamic SQL solution would be something like this:
DECLARE #Sql nvarchar(max);
SET #Sql = STUFF((
SELECT ' UNION ALL SELECT [ColumnsList], '''+ [name] + ''' As SourceDb FROM '+ QUOTENAME([name]) + '.[SchemaName].[TableName]' + char(10)
FROM master.sys.databases
WHERE [name] LIKE 'Cab%'
FOR XML PATH('')
), 1, 10, '');
--When dealing with dynamic SQL, print is your best friend...
PRINT #Sql
-- Once the #Sql is printed and you can see it looks OK, you can run it.
--EXEC(#Sql)
Notes:
Use quotename to protect against "funny" chars in identifiers names.
Replace [ColumnsList] with the actual list of columns you need.
There's no need for loops of any kind, just a simple stuff + for xml to mimic string_agg (which was only introduced in 2017).
I've thrown in the source database name as a "bonus", if you don't want it that's fine.
The Order by clause in the query that generates the dynamic SQL is meaningless for the final query, so I've removed it.

Query to Copy Table Data from local database to linked server database

I am trying to copy all rows of a table from the database of my local machine to linked server database.
This is the query I am using:
DECLARE #Qry nvarchar(MAX)
DECLARE #Server nvarchar(50)
SET #Server = '[LINKEDSERVER]'
SET #Qry = '
DECLARE #Qry2 nvarchar(max)
SET #Qry2 = N''
SET IDENTITY_INSERT RDB.dbo.Type ON
insert into RDB.dbo.Type (id, Name) Select ID,Name From
[LOCALSERVER].localdb.dbo.Type
SET IDENTITY_INSERT RDB.dbo.Type OFF''
EXEC ' + #Server + '.RDB.dbo.sp_executesql #Qry2'
EXEC SP_EXECUTESQL #Qry
But I am getting this error - please help what to do here to make it work:
Could not find server 'LOCALSERVER' 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.
and If remove the [LOCALSERVER] then I get:
Invalid object name 'localdb'.
Please help if any other dynamic query can work to copy table data from local server to linked server.
You are trying to run the sql statement in remote linked server. In that remote linked server, there is no linkedserver(entry in sys.servers) defined for your local server. Hence, you are getting the error.
You can define linked server for your server in the remote machine and execute statement.
DECLARE #Qry nvarchar(MAX)
DECLARE #Server nvarchar(50)
SET #Server = '[LINKEDSERVER]'
SET #Qry = '
DECLARE #Qry2 nvarchar(max)
SET #Qry2 = N''
SET IDENTITY_INSERT RDB.dbo.Type ON
insert into RDB.dbo.Type (id, Name) Select ID,Name From
[LOCALSERVER].localdb.dbo.Type
SET IDENTITY_INSERT RDB.dbo.Type OFF''
EXEC ' + #Server + '.RDB.dbo.sp_executesql #Qry2'
EXEC SP_EXECUTESQL #Qry

How to Embed Variable Value in SQL Server query

I need to be able to query Active Directory from SQL Server - see
https://www.mssqltips.com/sqlservertip/2580/querying-active-directory-data-from-sql-server/
The following runs fine:
DECLARE #eid nvarchar(5)
DECLARE #username nvarchar(5)
SET #eid='123'
SELECT #username=sAMAccountName FROM OpenQuery(ADSI,'SELECT sAMAccountName, employeeID FROM ''LDAP://mydomain.com/DC=mydomain,DC=com'' WHERE objectClass = ''User'' AND employeeID=123')
PRINT #username
How to replace the hardcoded 123 value with the actual value of the #eid, so that the modified code will run fine as well?
Thanks!
Annoyingly you have to build the SQL string ahead of time to add a parameter value into it and then execute the OpenQuery using that string. You must execute all of it as you would a dynamic query EXEC sp_executeSQL ...
Here's what I came up with:
DECLARE #eid nvarchar(5), #username nvarchar(5), #Query nvarchar(MAX)
SELECT #eid='123'
SELECT #Query = N'SELECT sAMAccountName FROM OpenQuery(ADSI, ''SELECT sAMAccountName, employeeID FROM ''''LDAP://mydomain.com/DC=mydomain,DC=com'''' WHERE objectClass = ''''User'''' AND employeeID=' + #eid + ''')'
SELECT #Query
EXEC sp_executeSQL #Query, #username OUTPUT
PRINT #username
I may not have the output quite right. Another option you have is to insert the output of the EXEC sp_executeSQL into a temptable and select the username from that value. For more info you can see this question.
Another option (and probably the easiest of all) is to move the WHERE outside of the open query and filter after you get the results back like this:
DECLARE #eid nvarchar(5), #username nvarchar(5), #Query nvarchar(MAX)
SELECT #eid='123'
SELECT #username = sAMAccountName--, employeeID
FROM OpenQuery(AD, 'SELECT sAMAccountName, employeeID FROM ''LDAP://mydomain.com/DC=mydomain,DC=com'' WHERE objectClass = ''User''')
WHERE employeeID = #eid
PRINT #username
I know this is an old question, but here is a way to work with output values (since you'll return only 1 value):
DECLARE #eid nvarchar(5), #username nvarchar(50), #Query nvarchar(MAX)
SET #Query = '(SELECT #username = sAMAccountName FROM OpenQuery(ADSI,''SELECT sAMAccountName, employeeID FROM ''LDAP://mydomain.com/DC=mydomain,DC=com'' WHERE objectClass = ''User'' AND employeeID=' + #eid +') AS tblADSI)'
EXEC sp_executesql #Query , N'#username nvarchar(50) out', #username out
SELECT #username As Outputs
This will assign the result of the OpenQuery execution, in the variable #username.
We tested for Store procedure in MSSQL 2012, but should work with MSSQL 2008+.
Microsoft Says that sp_executesql(Transact-SQL): Applies to: SQL Server (SQL Server 2008 through current version), Windows Azure SQL Database (Initial release through current release). (http://msdn.microsoft.com/en-us/library/ms188001.aspx)

SQL Server Select-Into stored procedure

I am trying to write a custom stored procedure to carry out a select into operation. I want to copy a table (or some columns from a table) from one database to another. I am using SQL Server 2012
CREATE Procedure select_into
AS
Begin
#selection varchar(128),
#newtabname varchar(128)
#fromtabname varchar(128)
Select selection,
INTO table1,
FROM table2,
WHERE selection = #selection AND table1 = #newtabname AND table2 =#fromtabname;
go
EXEC select_into, Ecode, relational_db.dbo.work, dbo.Work_Data;
I get an error message indicating a syntax error near the "." in relational_db.dbo.work.
I would appreciate any help in getting this right
You have a missing comma in parameter list and wrong syntax for procedure declaration. It should be::
CREATE Procedure select_into
(
#selection varchar(128),
#newtabname varchar(128),
#fromtabname varchar(128)
)
AS
Begin
BUT, in addition your syntax for an INSERT INTO contains extra commas and you cannot perform dynamic T-SQL that way.
Can I suggest you first learn TSQL's syntax for SQL Server.
Try something like this ...
CREATE Procedure select_into
#selection NVARCHAR(128),
#newtabname NVARCHAR(128),
#fromtabname NVARCHAR(128)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'Select ' + QUOTENAME(#selection) +
N' INTO ' + QUOTENAME(#newtabname) +
N' FROM ' + QUOTENAME(#fromtabname)
EXECUTE sp_executesql #sql
END

How to pass parameter values to a T-SQL query

I am using the following T-SQL query in SQL server 2005 (Management Studio IDE):
DECLARE #id int;
DECLARE #countVal int;
DECLARE #sql nvarchar(max);
SET #id = 1000;
SET #sql = 'SELECT COUNT(*) FROM owner.myTable WHERE id = #id';
EXEC (#sql) AT oracleServer -- oracleServer is a lined server to Oracle
I am not sure how to pass the input parameter #id to the EXEC query, and pass the count result out to #countVal. I saw some examples for Microsoft SQL server like:
EXEC (#sql, #id = #id)
I tried this for Oracle but I got a statement error:
OLE DB provider "OraOLEDB.Oracle" for linked server "oracleServer"
returned message "ORA-00936: missing expression"
Try this:
EXEC sp_executesql #sql, N'#id int', #id
More info at this great article: http://www.sommarskog.se/dynamic_sql.html
As for the output, your SELECT needs to look something like this:
SELECT #countVal = COUNT(id) FROM owner.myTable WHERE id = #id
I'm selecting 'id' instead of '*' to avoid pulling unnecessary data...
Then your dynamic sql should be something like this:
EXEC sp_executesql #sql,
N'#id int, #countVal int OUTPUT',
#id,
#countVal OUTPUT
This example is adapted from the same article linked above, in the section sp_executesql.
As for your Oracle error, you will need to find out the exact SQL that sp_executesql is sending to Oracle. If there is a profiler or query log in Oracle, that may help. I have limited experience with Oracle, but that would be the next logical step for troubleshooting your problem.
The quick and dirty way is to just build the string before using the EXEC statement, however this is not the recommended practice as you may open yourself up to SQL Injection.
DECLARE #id int;
DECLARE #countVal int;
DECLARE #sql nvarchar(max);
SET #id = 1000;
SET #sql = 'SELECT COUNT(*) FROM owner.myTable WHERE id = ' + #id
EXEC (#sql) AT oracleServer -- oracleServer is a lined server to Oracle
The correct way to do this is to use the system stored procedure sp_executesql as detailed by magnifico, and recommended by Microsoft in Books Online is:
EXEC sp_executesql #sql, N'#id int', #id
I don't know why you are pass id separately.
You could do the following
SET #sql = 'SELECT COUNT(*) FROM owner.myTable WHERE id = ' + #id
Edit: Don't do the above. Use parameterized sql statements.

Resources