Selecting a database from a variable - sql

So I have two databases that have no relationship between them. The first one is where my dbo.Clients exists and has a column of the database name of the second db . My thought was to select the dbName from the Clients then use that variable to select data from the second database.
The query doesnt run can some one shed a little light? Thanks.
#dbName varchar(50) OUTPUT,
#clientID varchar(50)
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT * FROM sql02.iproconfig4.dbo.Clients
SET #dbName = (SELECT Clients.ClientDatabase FROM sql02.iproconfig4.dbo.Clients WHERE ClientID = #clientID)
SELECT * FROM sql02.#dbName.dbo.Discovery
END

You will need to use dynamic SQL to accomplish this:
DECLARE #sql nvarchar(max)
SET #sql = 'SELECT * FROM sql02.' + #dbName + '.dbo.Discovery'
EXEC sp_executesql #sql

Related

Stored Procedure That Return Different Table According To Parameter

I want to write a stored procedure that takes #FirmId as a parameter and I will use the related table according to this parameter.
What I want to obtain (but I don't want to use) is something like that:
CREATE PROCEDURE spFirmDetailGetByFirmId
#FirmId AS INT
AS
BEGIN
IF #FirmId = 1
SELECT * FROM Firm1
ELSE IF #FirmId = 2
SELECT * FROM Firm2
.
.
.
.
ELSE IF #FirmId = 1000
SELECT * FROM Firm1000
END
And also I don't want to create query string and then EXEC it, something like that in the fallowing code block. Because the real query is too complex and it will be very hard to manage if I use this option.
CREATE PROCEDURE spFirmDetailGetByFirmId
#FirmId AS INT
AS
BEGIN
DECLARE #Query AS NVARCHAR(MAX) = 'SELECT * FROM Firm'
SET #Query = #Query + CAST(#FirmId AS NVARCHAR(10))
EXEC(#Query)
END
Is there any other option?
Thanks.
I take your Yes the tables are identical and will be kept identical to suggest two approaches:
DECLARE #Firm VARCHAR(10)='Firm3';
SELECT * FROM Firm1 WHERE #Firm='Firm1'
UNION ALL
SELECT * FROM Firm2 WHERE #Firm='Firm2'
UNION ALL
SELECT * FROM Firm3 WHERE #Firm='Firm3'
[...]
UNION ALL
SELECT * FROM Firm1000 WHERE #Firm='Firm1000'
The second is:
DECLARE #query NVARCHAR(MAX)='SELECT * FROM ####';
SET #query=REPLACE(#query,'####',#Firm);
EXEC (#query)
The second could be used with a VIEW (in place of the #query), where you could read the VIEW's definition into the variable and create an ALTER VIEW-statement dynamically... Your procedure would call the same VIEW (but this would crash with parallel calls!)
This code can by use in a stored procedure to automatic create the view, every time you need to add columns
declare #tableId int
declare #columns varchar(max)
declare #tablesCount int
declare #tableName varchar(255)
declare #query varchar(255)
declare #id int
declare #result nvarchar(max)
set #columns = ''
set #tableName = 'Firm'
set #id = 1
set #result = ''
--Base table
select #tableId = object_id from sys.tables where name =#tableName
--Count how many table with the 'same name'
select #tablesCount= count(*) from sys.tables where name like #tableName+'%'
--Build Columns to add in the view
select #columns =#columns+name+', 'from Sys.columns where object_id = #tableId
--Drop View
set #result = 'Drop view vw_'+#tableName
exec sp_executesql #result
set #result=''
while(#id<=#tablesCount)
Begin
declare #idVarchar varchar(10)
set #idVarchar = cast(#id as varchar(10))
set #result =#result+'Select '+#columns+#idVarchar+' as FirmId from '+#tableName+#idVarchar
+'
Union all
'
set #id =#id+1
End
set #result = substring(#result, 1, len(#result)-12)
set #result='Create view vw_'+#tableName+' as
'+#result
exec sp_executesql #result
There is a another choice to this, you can also use sp_helpText to get the current definition of the view and append only add new table identifier

Using variables in Transact-sql exists subquery

this seems like it should be extraordinarily simple, so I apologize in advance if this information is easily accessible on the transact-sql documentation pages. I searched myself, but couldn't seem to find anything.
I'm trying to modify a transact-sql statement that currently runs on our Windows server 2000 box. I want to check if a table in another database exists, and then do a bunch of stuff. The database name is given as a string argument, '#dbName'
CREATE PROCEDURE CopyTables
#dbName char(4)
AS
IF EXISTS (SELECT * FROM #dbName.INFORMATION_SCHEMA.TABLES WHERE
TABLE_NAME = N'MainTable')
BEGIN
--Do Stuff
In it's current state, it doesn't like using the bare #dbName variable within the select statement. Is there special syntax for doing this?
Thanks in advance.
The below code should do what you want. As was mentioned previously, the account running the query would need the privilege to query the INFORMATION_SCHEMAs in the target database.
To future-proof your stored procedure, I'd also suggest increasing the length of the database name parameter and declaring it as an nchar or nvarchar in stead of char.
CREATE PROCEDURE CopyTables
#dbName char(4)
AS
DECLARE
#SQLStr nvarchar (max),
#Params nvarchar (max),
#Count tinyint;
SET
#Count = 0;
SET #SQLStr = N'SELECT #qCount = 1 FROM [' + #dbName + N'].INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N''MainTable''';
SET #Params = N'#qdbName char (4), #qCount tinyint OUTPUT';
EXECUTE sp_executesql #SQLStr, #Params, #qdbName = #dbName, #qCount = #Count OUTPUT;
IF #Count = 1
BEGIN
--Do Stuff
END; -- if
GO
Try doing the following:
DECLARE #dbName NVARCHAR(MAX) = 'master', #TableName NVARCHAR(MAX) = N'spt_monitor';
DECLARE #sql NVARCHAR(MAX) = N'SELECT * FROM [' + #dbName + N'].INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ''' + REPLACE(#TableName,N'''',N'''''') + N'''';
SET NOCOUNT OFF;
EXEC(#sql);
IF ##ROWCOUNT > 0 BEGIN;
-- DO STUFF
SELECT NULL;
END;
There are a few shortcomings to this solution:
1) It requires that the user executing the statement has SELECT access to the other database's INFORMATION_SCHEMA.TABLES
2) It has the side-effect of actually selecting the rows, so if you're using a reader to access the results, you'll have to call reader.NextResult() or await reader.NextResultAsync() because it actually outputs the results of the SELECT statement, rather than doing it in an IF EXISTS context.
By merging the two solutions, we get this:
DECLARE #dbName NVARCHAR(MAX) = 'master', #TableName NVARCHAR(MAX) = N'spt_monitor';
DECLARE #sql NVARCHAR(MAX) = N'SELECT #count = COUNT(*) FROM [' + #dbName + N'].INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ''' + REPLACE(#TableName,N'''',N'''''') + N'''';
DECLARE #Count INT;
EXECUTE sp_executesql #sql, N'#Count INT OUTPUT', #Count OUTPUT;
IF #Count > 0 BEGIN;
-- Do stuff
SELECT 'the table exists';
END ELSE BEGIN;
-- Do stuff
SELECT 'the table does not exist';
END;
This solution requires that the user executing the statement has SELECT access to the other database's INFORMATION_SCHEMA.TABLES, but it does not have the side-effect of selecting rows, like my previous solution.

Can we pass database name in a SQL query as parameter?

Consider the following queries, where only database name differs (on same server)
Select * from sampledev.dbo.Sample
Select * from sampleqa.dbo.Sample
The above queries are part of a procedure. Every time I have to run the procedure, I have to make sure it references the correct database (and do rename, if it is not).
I want to pass the database name as a parameter to the stored procedure. The question is, is it possible? If yes, how?
You can accomplish this using sp_executesql
DECLARE #Database NVARCHAR(255),
#Query NVARCHAR(MAX)
SET #Database = 'Database'
SET #Query = N'SELECT * FROM ' + #Database + '.dbo.Table'
EXEC sp_executesql #Query
Something as simple as: ?
CREATE PROC GetData
(
#DatabaseName VARCHAR(255)
)
AS
BEGIN
IF #DatabaseName = 'sampledev'
SELECT * FROM sampledev.dbo.Sample
ELSE IF #DatabaseName = 'sampleqa'
SELECT * FROM sampleqa.dbo.Sample
END
Use:
exec GetData 'sampledev'
Results
dev data
(1 row(s) affected)
exec GetData 'sampleqa'
Results
qa data
(1 row(s) affected)
Just like the leading answer but without SQL injection vulnerability.
First you must query the sys.databases in order to be sure to get the real Database name while not counting on the users text:
SELECT #Database = [name]
FROM sys.databases
WHERE [name] = #Database;
Now perform the query using sp_executesql:
DECLARE #Query nvarchar(200);
SET #Query = N'SELECT * FROM ' + #DBName + '.dbo.sample';
EXEC sp_executesql #Query
Full Stored procedure:
CREATE PROCEDURE [MyScheme].[MyStoredProcedure]
(
#DBName sysname
)
AS
BEGIN
SET NOCOUNT ON;
SELECT #DBName = [name]
FROM sys.databases
WHERE [name] = #DBName;
DECLARE #Query nvarchar(200);
SET #Query = N'SELECT * FROM ' + #DBName + '.dbo.sample';
EXEC sp_executesql #Query
END
GO

How to specify a table dynamically in a Stored Procedure

Thanks for the feedback, but I was hoping for help with an UPDATE command, not SELECT.
Can anyone help with the syntax for an UPDATE command?
I am passing a table name into a Stored Procedure but the SQL does not seem to recognize it.
DECLARE #userTable AS VARCHAR(200);
SET #userTable = #currTable
UPDATE #userTable
SET [lang_String] = #lang_String, [date_Changed] = #submitDate1
WHERE (ID = #ID)
#currTable is passed into the Stored Procedure. All tables names are built by design in code.
You can't, you need to build the entire SQL string and then execute it, like this for example:
DECLARE #sql nvarchar(4000)
SELECT #sql = ' SELECT col1, col2, col3 ' +
' FROM dbo.' + quotename(#tblname) +
' WHERE keycol = #key'
EXEC sp_executesql #sql, N'#key varchar(10)', #key
Got this to work quite easily....
#myTable varchar(150)
/* Comments:
*/
AS
SET NOCOUNT ON;
DECLARE #sql varchar(max);
SET #sql = 'SELECT [ID], [StringID], [GUID] FROM ' + #myTable + ' ORDER BY [GUID]';
print (#sql)
EXECUTE(#sql);
SET #langTable = Null;
FYI, the values available for myTable are stored in another table and are not available to users for edit. Table names are built dynamically in code based on a unique combination of values.

using dynamic IN clause in MSSQL

Why the following SQL does not fetch me anything
DECLARE #Status AS VARCHAR(400)
SET #status = '''Closed'',''OPEN'''
select * from MYTABLE where status in(#status)
While as
select * from MYTABLE where status in('Closed','Open') fetches me rows
You can if you want do some dynamic SQL but I think it is not really competitive..
DECLARE #Status nVARCHAR(400),
#SQL nvarchar(500)
SET #status = '''Closed'''+','+'''OPEN'''
set #SQL = '
select * from [MYTABLE] where status in('+#status +')'
exec sp_executesql #SQL
GO
Your first question checks if the value 'Closed','OPEN' exists in the database. The values is not expanded.
If you use SQL Server 2008 or later you can use Table Valued Parameters to achieve the same thing.