SQL Synonym for same table different db name - sql

we have a few procedures in DatabaseA that reference tables in DatabaseX, but unfortunately, in different environments we have prefixes to DatabaseA and DatabaseX such that when we deploy to an environment, we need to amend those references. DatabaseA's naming convention is like "Test#DatabaseA" similarly DatabaseX is "Test#DatabaseX"
Can I create a single synonym for something like this, or would I need to create a synonym for each environment/prefix for DatabaseX, or a script like what I've found.
declare #Server sysname,
#db sysname,
#Schema sysname,
#Table sysname,
#statement varchar(max)
SELECT #Server = 'ServerName',
#db = LEFT(DB_NAME(),LEN(DB_NAME())-CHARINDEX('DatabaseA',DB_NAME())+1)+'DatabaseX',
#Schema = 'SchemaName',
#Table = 'TableName'
set #statement = 'CREATE SYNONYM synonym_Name FOR '+
#Server +'.'+
#db +'.' +
#Schema + '.' +
#Table
EXEC (#statement)

Related

Stored procedure to copy table using dynamic query in SQL Server

I want to create a stored procedure for coping table using dynamic query.
I followed this step for creating stored proceduce, link:-https://stackoverflow.com/questions/8698231/sql-server-stored-procedures-to-copy-tables
but I got an error:
Could not find stored procedure 'Select * into tblDetail from salesDetail'
Here is my code:
ALTER PROCEDURE sp_copyOneTableToAnother
#newtable nvarchar(50),
#oldtable nvarchar(50)
AS
BEGIN
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * INTO ' + #newtable +
' FROM ' + #oldtable
EXEC #sql
END
exec sp_copyOneTableToAnother #newtable='tblDetail',#oldtable='salesDetail'
The stored procedure was created from above syntax but while calling sp_copyOneTableToAnother, I get an error. Please help me solve it.
There are several problems here, first, your procedure name starts with sp_, which is reserved by Microsoft for Special / System Procedures. That should go.
Next, your parameter types are wrong; the correct data type for an object is a sysname, a synonym of nvarchar(128) NOT NULL, not varchar.
Next, the injection issue; you blindly inject the values of your parameters into your statement and hope that said values aren't malicious. Validate the value of #oldtable and properly quote both parameters.
Finally, the execution should be done by sp_executesql; not using it promotes further injection issues as you can't parametrise EXEC (#SQL) statements.
You also don't define your schemas, which you really should be. I add them as NULLable parameters here, and get the USER's default schema
This results in something like this:
CREATE OR ALTER PROCEDURE dbo.CopyOneTableToAnother #NewTable sysname,
#OldTable sysname,
#NewSchema sysname = NULL,
#OldSchema sysname = NULL AS
BEGIN
SET NOCOUNT ON;
SELECT #NewSchema = ISNULL(#NewSchema,default_schema_name),
#OldSchema = ISNULL(#OldSchema,default_schema_name)
FROM sys.database_principals
WHERE name = USER_NAME();
DECLARE #SQL nvarchar(MAX);
SELECT #SQL = N'SELECT * INTO ' + QUOTENAME(#NewSchema) + N'.' + QUOTENAME(#NewTable) + N' FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name]) + N';'
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE s.name = #OldSchema
AND t.[name] = #OldTable;
EXEC sys.sp_executesql #SQL;
END;
db<>fiddle

How to avoid hard coding DB name in SQL synonym creation

Working with SQL Server 2008 R2. I have a script full of following statements:
create synonym [synonym_name] for [linkedServerName].[databaseName].[ext].[tableName1]
create synonym [synonym_name] for [linkedServerName].[databaseName].[ext].[tableName2]
Question:
1. Is there any way to avoid 4 part naming. especially hard coded DB name
There is an existing script for LinkedServer creation. Can this be used while creating synonyms?
What would be the best way to create synonyms for multiple Table/Views?
declare #Server sysname,
#db sysname,
#Schema sysname,
#Table sysname,
#statement varchar(max)
SELECT #Server = 'Venice',
#db = 'venice1',
#Schema = 'ext'
EXEC('CREATE SYNONYM synonym_name1 FOR '+ #Server +'.'+ #db +'.' + #Schema + '.Table1')
EXEC('CREATE SYNONYM synonym_name2 FOR '+ #Server +'.'+ #db +'.' + #Schema + '.Table2')
Thoughts: Is this the only way?
Try this:
declare #Server sysname,
#db sysname,
#Schema sysname,
#Table sysname,
#statement varchar(max)
SELECT #Server = 'ServerName',
#db = 'DatabaseName',
#Schema = 'SchemaName',
#Table = 'TableName'
set #statement = 'CREATE SYNONYM synonym_Name FOR '+
#Server +'.'+
#db +'.' +
#Schema + '.' +
#Table
EXEC (#statement)
Yes that's the only way. Per MSDN documentation the Synonym creation syntax requires the DB name as can be seen below unless you are taking help of dynamic SQL.
-- SQL Server Syntax
CREATE SYNONYM [ schema_name_1. ] synonym_name FOR <object>
<object> :: =
{
[ server_name.[ database_name ] . [ schema_name_2 ]. object_name
}

Incorrect syntax SQL

Why do I get this error when I try to execute the following code?
I have a table NewTable1 with two columns: column1 and column2.
I get this error: Incorrect syntax near 'column2'.
--DROP COLUMN PROCEDURE
CREATE PROCEDURE DropColumn
#tableName varchar(50),
#columnName varchar(50)
AS
BEGIN
DECLARE #SQL nvarchar(500);
SET #SQL = N'ALTER TABLE ' + QUOTENAME(#tableName)
+ ' DROP COLUMN ' + QUOTENAME(#columnName);
EXEC sp_executesql #SQL;
END
RETURN 0
GO
USE SKI_SHOP;
EXEC DropColumn 'NewTable1', 'column2';
GO
Use appropriate data types. Also You will only be able to drop Columns for tables in callers default schema. Since procedure doesn't take schema into consideration, therefore you can only pass the table name and if a table exists in other than caller default schema they wont be able to delete it using this procedure .
CREATE PROCEDURE DropColumn
#tableName SYSNAME,
#columnName SYSNAME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N' ALTER TABLE ' + QUOTENAME(#tableName)
+ N' DROP COLUMN ' + QUOTENAME(#columnName);
EXEC sp_executesql #SQL;
END
GO
I over looked some basic simple issues in my first approach, whenever creating of Dropping objects in SQL Server always check if they exist, to avoid any errors . A more complete and safe approach would be something like ...
This time I have also added schema as a parameter.
ALTER PROCEDURE DropColumn
#tableName SYSNAME,
#columnName SYSNAME,
#Schema SYSNAME,
#Success BIT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N' IF EXISTS (SELECT * FROM sys.tables t
INNER JOIN sys.columns c
ON t.[object_id] = c.[object_id]
INNER JOIN sys.schemas sc
ON t.[schema_id] = sc.[schema_id]
WHERE t.name = #tableName
AND c.name = #columnName
AND sc.name = #Schema)
BEGIN
ALTER TABLE ' + QUOTENAME(#Schema)+ '.' + QUOTENAME(#tableName)
+ N' DROP COLUMN ' + QUOTENAME(#columnName)
+ N' SET #Success = 1; '
+ N' END
ELSE
BEGIN
SET #Success = 0;
END '
EXEC sp_executesql #SQL
,N'#tableName SYSNAME, #columnName SYSNAME, #Schema SYSNAME, #Success BIT OUTPUT'
,#tableName
,#columnName
,#Schema
,#Success OUTPUT
END
GO

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 secure dynamic SQL stored procedure?

I have a stored procedure that takes in the name of a table as a parameter and uses dynamic sql to perform the select. I tried to pass #TableName as a parameter and use sp_executesql but that threw an error. I decided to go with straight dynamic sql without using sp_executesql.
Is there anything else I should be doing to secure the #TableName parameter to avoid sql injection attacks?
Stored procedure below:
CREATE PROCEDURE dbo.SP_GetRecords
(
#TableName VARCHAR(128) = NULL
)
AS
BEGIN
/* Secure the #TableName Parameter */
SET #TableName = REPLACE(#TableName, ' ','')
SET #TableName = REPLACE(#TableName, ';','')
SET #TableName = REPLACE(#TableName, '''','')
DECLARE #query NVARCHAR(MAX)
/* Validation */
IF #TableName IS NULL
BEGIN
RETURN -1
END
SET #query = 'SELECT * FROM ' + #TableName
EXEC(#query)
END
This failed when using sp_executesql instead:
SET #query = 'SELECT * FROM #TableName'
EXEC sp_executesql #query, N'#TableName VARCHAR(128)', #TableName
ERROR: Must declare the table variable
"#TableName".
See here:
How should I pass a table name into a stored proc?
you of course can look at the sysobjects table and ensure that it exists
Select id from sysobjects where xType = 'U' and [name] = #TableName
Further (more complete example):
DECLARE #TableName nVarChar(255)
DECLARE #Query nVarChar(512)
SET #TableName = 'YourTable'
SET #Query = 'Select * from ' + #TableName
-- Check if #TableName is valid
IF NOT (Select id from sysobjects where xType = 'U' and [name] = #TableName) IS NULL
exec(#Query)