Get database name dynamically - sql-server

I am trying to get the database name dynamically but, showing some errors.
What I have tried:
Declare #dbname varchar(40) = (select name from sys.databases where name like '%worksdw2019')
select db_name() as [Database], *
from #dbname.dbo.DimCustomer with (readuncommitted)
How end result should look like
select db_name() as [Database], *
from AdventureWorksdw2019.dbo.dimcustomer
These are the errors I am getting:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '.'.
Msg 319, Level 15, State 1, Line 3
Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon.

You can't parameterize database names, so you'll need dynamic SQL.
DECLARE #context nvarchar(1000);
SELECT #context = QUOTENAME(name) + N'.sys.sp_executesql'
from sys.databases
where name like N'%worksdw2019';
-- what if there is more than one?
DECLARE #sql nvarchar(max) = N'select db_name() as [Database], *
from dbo.DimCustomer;';
EXECUTE #context #sql;
Uh, with (readuncommitted) -> why? sqlblog.org/nolock

You have invalid SQL written.
You cannot write use a variable like that in SQL Server. If the database name is going to be variable as seen in your example, you will need to execute dynamic SQL using the EXEC command such as this example below:
Declare #dbname varchar(40) = (select name from sys.databases where name like '%HelloWorld')
EXEC('select db_name() as [Database], * from ' + #dbname + '.dbo.DimCustomer with (readuncommitted);')
-- you may additionally need to escape your database name
-- if you are using special characters in your name

Related

Dynamic SQL server query with variables (Problems executing)

This is a far smaller version of a query that basically needs a variable of the table on which everything is run.
When I run the procedure I get the error message:
Msg 156, Level 15, State 1, Line 2
Incorrect syntax near the keyword 'table'.
alter procedure james_tester
#tablename nvarchar(200)
as
BEGIN
declare #sql nvarchar(max)
set #sql =
'
select * from '
+ #tablename
EXECUTE sp_executesql #sql
END
To fix this i tried using things like quotename and played around with the format but nothing seems to have worked yet.
execute james_tester 'dbo.Calendar table' (That is the table I am wanting to query)
The problem lies in how you call your procedure.
execute james_tester 'dbo.Calendar table'
should be
execute james_tester 'dbo.Calendar'
Hence the error message :
Incorrect syntax near the keyword 'table'.
Table is a keyword of sql server.
So you couldn't use it as an alias with out [].
Try this
execute james_tester 'dbo.Calendar [table]'
or
execute james_tester 'dbo.Calendar t'
or
execute james_tester 'dbo.Calendar'

SQL variable for Database Name

I am trying to pass a database name in as a parameter and execute some dynamic SQL. As a test I created this:
declare #HRMSDatabase_1 nvarchar(50) = N'FirstDatabase',
#Example_1 nvarchar(max) =
'select #HRMSDatabase'
execute sp_executesql #Example_1, N'#HRMSDatabase nvarchar(50)', #HRMSDatabase_1
which returns FirstDatabase as I expected.
When I try this:
declare #HRMSDatabase_2 nvarchar(50) = N'FirstDatabase',
#Example_2 nvarchar(max) =
'select
''Test''
from
#HRMSDatabase.dbo.hrpersnl hp'
execute sp_executesql #Example_2, N'#HRMSDatabase nvarchar(50)', #HRMSDatabase_2
I get an error message:
Msg 102, Level 15, State 1, Line 29
Incorrect syntax near '.'.
Is what I am trying to do possible? I cannot simply use a USE FirstDatabase as I have a few databases I have to query in the same dynamic SQL using inner joins.
Also, I cannot use SQLCMD as this script gets executed from a GUI.
Basically, I don't believe you can parameterize the database name in the table specifier. Instead try this,
DECLARE #HRMSDatabase NVARCHAR(50) = N'FirstDatabase';
DECLARE #Example3 NVARCHAR(MAX) ='SELECT
''Test''
FROM
' + QUOTENAME(#HRMSDatabase) + '.[dbo].[hrpersnl] hp';
EXEC sp_executesql #Example3;
As you'll note, it's important that the #HRMSDatabase is not recieved from user input as this would be susceptible to injection attacks.

Why we should use QUOTENAME function?

I get acquainted with QUOTENAME function. But I don't understand for what I can use it? Why it is so widely used?
select quotename('[abc]') -- '[[abc]]]'
select quotename('abc') -- '[abc]'
select '[' + 'abc' +']' -- why it is not so good as previous?
Imagine the following script is scheduled to run regularly to clean up tables in schemas other than the dbo schema.
DECLARE #TABLE_SCHEMA SYSNAME,
#TABLE_NAME SYSNAME
DECLARE #C1 AS CURSOR;
SET #C1 = CURSOR FAST_FORWARD
FOR SELECT TABLE_SCHEMA,
TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA <> 'dbo'
OPEN #C1;
FETCH NEXT FROM #C1 INTO #TABLE_SCHEMA, #TABLE_NAME;
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'DROP TABLE [' + #TABLE_SCHEMA + '].[' + #TABLE_NAME + ']';
EXEC ('DROP TABLE [' + #TABLE_SCHEMA + '].[' + #TABLE_NAME + ']');
FETCH NEXT FROM #C1 INTO #TABLE_SCHEMA, #TABLE_NAME;
END
If you create the following and run the script then all works as expected despite using the manual string concatenation approach. The table foo.bar is dropped.
CREATE SCHEMA foo
CREATE TABLE foo.bar(x int)
Now create the following and try
CREATE TABLE foo.[[abc]]](x int)
The script fails with an error
DROP TABLE [foo].[[abc]]
Msg 105, Level 15, State 1, Line 6
Unclosed quotation mark after the character string '[abc]'.
Msg 102, Level 15, State 1, Line 6
Incorrect syntax near '[abc]'.
So not using QUOTENAME has caused the script to fail. The closing bracket was not escaped properly by doubling it up. The correct syntax should have been
DROP TABLE [foo].[[abc]]]
Even worse news is that a malicious developer has come to know of the script's existence. They execute the following just before the script is scheduled to run.
CREATE TABLE [User supplied name]];
EXEC sp_addsrvrolemember 'SomeDomain\user2216', 'sysadmin'; --]
(
x int
)
Now the script that ends up being executed is
DROP TABLE [foo].[User supplied name];
EXEC sp_addsrvrolemember 'SomeDomain\user2216', 'sysadmin'; --]
The ] was interpreted as closing off the object name and the remainder as a new statement. The first statement returned an error message but not a scope terminating one and the second one was still executed. By not using QUOTENAME you have opened yourself up to SQL injection and the developer has successfully escalated their privileges
QUOTENAME can be used when generating dynamic SQL statements. It'll indeed place your column name between brackets but will also escape characters which would break your quoted column name and could cause SQL injection.
For example:
SELECT QUOTENAME('abc[]def');
Will return:
[abc[]]def]
For more info: QUOTENAME (MSDN)

SQL server create stored procedure syntax error

I am trying to create a simple stored procedure:
CREATE PROCEDURE SP_Test #FilePath int
AS
SELECT
LastName, FirstName
INTO
tmp_tblPerson
FROM
OPENROWSET('MSDASQL','Driver={Microsoft Access Text Driver (.txt, .csv)}','SELECT * FROM ' + #FilePath + "'")
GO
But I get a syntax error which I don't understand..?
Msg 102, Level 15, State 1, Procedure SP_Test, Line 12
Incorrect syntax near '+'.
Any ideas?
You can't use dynamic SQL when using using OPENROWSET. A workaround is to make the entire block use dynamically created SQL like this:
CREATE PROCEDURE SP_Test #FilePath int
AS
DECLARE #sql NVARCHAR(MAX) =
'SELECT LastName, FirstName
INTO tmp_tblPerson
FROM OPENROWSET(
''MSDASQL'',
''Driver={Microsoft Access Text Driver (.txt, .csv)}'',
''SELECT * FROM '' + #FilePath)'
EXEC(#sql)
As always with dynamic SQL, make sure you are not vulnerable to SQL injection attacks.
Additionally, your query appears to be incorrect as I doubt you have a table with an integer as a name.
#filepath is int, you probably want something like
'SELECT * FROM ' + convert(varchar,#FilePath)

TSQL Collect data from multiple databases

Recently started learning SQL and ran into this problem that i'm unable to find solution.
I'm trying to collect data from multiple databases and output it to single table.
Source select outputs data in two columns "acAcct" and "name of source db"
Target table has "acAcct" and column for each db.
Thing that i came up with:
DECLARE #command varchar(1000)
SET #command = 'IF ''[?]'' NOT IN (''[master]'', ''[model]'', ''[msdb]'', ''[tempdb]'')
INSERT INTO _utility.dbo.konta (acAcct, ''?'')
SELECT DISTINCT AC.acAcct
,AD.acName AS ''?''
from tHE_AcctTransItem as AC
LEFT JOIN vDE_SetAccount as AD
ON AC.acAcct = AD.acAcct'
EXEC sp_MSforeachdb #command
I get this error for each DB:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near 'name of DB'.
I guess i'm missing something with parsing of variable at:
INSERT INTO _utility.dbo.konta (acAcct, ''?'')
Select works fine.
EDIT:
Ended up with this:
DECLARE #command varchar(1000)
SET #command = 'IF ''[?]'' NOT IN (''[master]'', ''[model]'', ''[msdb]'', ''[tempdb]'')
MERGE _utility.dbo.konta AS target
USING (SELECT DISTINCT AC.acAcct
,AD.acName AS [?]
from [?].dbo.tHE_AcctTransItem as AC
LEFT JOIN [?].dbo.vDE_SetAccount as AD
ON AC.acAcct = AD.acAcct) as source
ON (target.acAcct = source.acAcct)
WHEN MATCHED THEN UPDATE SET target.[?] = source.[?]
WHEN NOT MATCHED THEN
INSERT (acAcct, [?])
VALUES (source.acAcct, [?]);
'
EXEC sp_MSforeachdb #command
Original script filled only first column and errored out on others.
If the source database name is a column then it should not be enclosed in single quotes. Square brackets would be safer.
DECLARE #command varchar(1000)
SET #command = 'IF ''[?]'' NOT IN (''[master]'', ''[model]'', ''[msdb]'', ''[tempdb]'')
INSERT INTO _utility.dbo.konta (acAcct, [?])
SELECT DISTINCT AC.acAcct
,AD.acName AS [?]
from tHE_AcctTransItem as AC
LEFT JOIN vDE_SetAccount as AD
ON AC.acAcct = AD.acAcct'
EXEC sp_MSforeachdb #command

Resources