Using synonyms in stored procedures - sql

I have a synonym for a table in another DB defined
using
create synonym TableA for otherDb.dbo.TableA
I have a locally defined stored procedure
CREATE PROCEDURE dbo.spGetTableA
AS
BEGIN
SELECT * FROM TableA
END
Now when I call the SP
EXEC spGetTableA
I get the following error
Invalid object name 'TableA'
While calling the SQL directly SELECT * FROM TableA
works perfectly.
Any idea what I'm missing for this to work?

You are probably calling the stored procedure from a user whose default schema is not dbo. Therefore you should always reference the schema both when you create the synonym and when you reference the table in a query.
DROP SYNONYM TableA;
GO
CREATE SYNONYM dbo.TableA FOR OtherDB.dbo.TableA;
GO
ALTER PROCEDURE dbo.spGetTableA
AS
BEGIN
SELECT * FROM dbo.TableA;
END
GO
EXEC dbo.spGetTableA;
I wish I could bold all of those dbo. references within the code. They are important and should ALWAYS be there.
Please read:
Bad habits to kick : avoiding the schema prefix

Related

Stored Procedure Select data from Server A and Insert data to Server B

I would like to ask about how to write the Stored Procedure Select data from Server A and Insert data to Server B?
1 . Create a linked server using steps at below link
https://www.sqlshack.com/how-to-create-and-configure-a-linked-server-in-sql-server-management-studio/
Create a synonym for the table from the linked server you are going to use if there is only one table otherwise create synonym for that server so you dont have to use the complete name and if the server changes in future you dont have to go and change in every procedure instead you can just change the synonym.
CREATE SYNONYM MySyn
FOR LinkedServerName.DatabaseName.SchemaName;
GO
Use the synonym in your query to fetch the data.
create procedure procedureName
as
begin
insert into destDB.schema.tablename select * from mysyn.sourcetablename
end

How to redirect a request for DB1 to DB2

Suppose I have two databases named DB1 and DB2. In DB1, there is a table named Student, In DB2, there is a stored procedure named SP1. In SP1, I am selecting data of Student Table using below query :
Select *from DB1.dbo.Student.
I have more than 300 stored procedures having above said cross database communication. Now, I want to change my database from DB1 to DB3 that is identical to DB1 from data and schema perspective.
For this, I also have to modify all 300 stored procedures that are having fully-qualified database name. Now, the query will likely to be as follows :
Select *from DB3.dbo.Student
I don't want to change all stored procedure to point DB3 now, also don't want to change my queries written in stored procedure into dynamic SQL (I know this can be done by creating dynamic SQL).
Is it possible if We run DB1.dbo.Student, It will redirect to DB3.dbo.Student. Any intermediate layer or any SQL setting.
It'll be very big help for me. Thanks In Advance !!
If the purpose of your database renaming is to migrate a database, then why not rename the databases themselves?
e.g. say rename DB1 to DB1_old and then rename DB3 to DB1
I would simply script out all stored procedures using SQL Server script generator tool. Then do a find replace on the script and find text ‘DB1.dbo.’ and replace with ‘DB3.dbo.’
In the future you might want to consider using synonyms to reference external tables then you would only have to update the synonyms instead of all of your procedures. Please see following MSDN article on synonyms:
https://msdn.microsoft.com/en-us/library/ms187552.aspx
Example use of synonym:
USE [DB1]
GO
-- Create a synonym for table A located in DB2.
CREATE SYNONYM [dbo].[External_TableA] FOR [DB2].[dbo].[TableA]
GO
-- Synonym is pointing to TableA in DB2 , select statement will return data from DB2 tabla A.
SELECT *
FROM [External_TableA]
GO
-- Point the Synonym to same table but on DB3
DROP SYNONYM [dbo].[External_TableA]
CREATE SYNONYM [dbo].[External_TableA] FOR [DB3].[dbo].[TableA]
GO
-- No update was needed on views or stored procedure.
-- Synonym is pointing to TableA in DB3 , select statement will return data from DB3 tabla A.
SELECT *
FROM [External_TableA]
The follow query will generate the required DROP and CREATE script to remap your synonyms from the old database to the new database.
DECLARE #oldDB NVARCHAR(100) = 'DB2';
DECLARE #newDB NVARCHAR(100) = 'DB3';
SELECT 'DROP SYNONYM [dbo].[' + name + ']' AS [Drop Script]
,'CREATE SYNONYM [dbo].[' + name + '] FOR ' + REPLACE(base_object_name, #oldDB, #newDB) AS CreateScript
FROM sys.synonyms
ORDER BY name
its better to use USE Keyword
use [database name you want to access]
Queries and stored procedure you want to use
GO
eg
use [db1]
select *from yourTableName
exec yourStoredProcedure parm1,parm2,....
Go

What's the scoping rule for temporary tables within exec within stored procedures?

Compare the following stored procedures:
CREATE PROCEDURE testProc1
AS
SELECT * INTO #temp FROM information_schema.tables
SELECT * FROM #temp
GO
CREATE PROCEDURE testProc2
AS
EXEC('SELECT * INTO #temp FROM information_schema.tables')
SELECT * FROM #temp
GO
Now, if I run testProc1, it works, and #temp seems to only exist for the duration of that call. However, testProc2 doesn't seem to work at all, since I get an Invalid object name '#temp' error message instead.
Why the distinction, and how can I use a temp table to SELECT * INTO if the source table name is a parameter to the stored procedure and can have arbitrary structure?
Note that I'm using Microsoft SQL Server 2005.
From BOL:
Local temporary tables are visible
only in the current session... ...
Temporary tables are automatically
dropped when they go out of scope,
unless explicitly dropped using DROP
TABLE
The distinction between your first and second procedures is that in the first, the table is defined in the same scope that it is selected from; in the second, the EXEC() creates the table in its own scope, so the select fails in this case...
However, note that the following works just fine:
CREATE PROCEDURE [dbo].[testProc3]
AS
SELECT * INTO #temp FROM information_schema.tables
EXEC('SELECT * FROM #temp')
GO
And it works because the scope of EXEC is a child of the scope of the stored procedure. When the table is created in the parent scope, it also exists for any of the children.
To give you a good solution, we'd need to know more about the problem that you're trying to solve... but, if you simply need to select from the created table, performing the select in the child scope works just fine:
CREATE PROCEDURE [dbo].[testProc4]
AS
EXEC('SELECT * INTO #temp FROM information_schema.tables; SELECT * FROM #temp')
GO
You could try using a global temp table (named ##temp not #temp). However be aware that other connections can see this table as well.

Why does Microsoft SQL Server check columns but not tables in stored procs?

Microsoft SQL Server seems to check column name validity, but not table name validity when defining stored procedures. If it detects that a referenced table name exists currently, it validates the column names in a statement against the columns in that table. So, for example, this will run OK:
CREATE PROCEDURE [dbo].[MyProcedure]
AS
BEGIN
SELECT
Col1, Col2, Col3
FROM
NonExistentTable
END
GO
... as will this:
CREATE PROCEDURE [dbo].[MyProcedure]
AS
BEGIN
SELECT
ExistentCol1, ExistentCol2, ExistentCol3
FROM
ExistentTable
END
GO
... but this fails, with 'Invalid column name':
CREATE PROCEDURE [dbo].[MyProcedure]
AS
BEGIN
SELECT
NonExistentCol1, NonExistentCol2, NonExistentCol3
FROM
ExistentTable
END
GO
Why does SQL Server check columns, but not tables, for existence? Surely it's inconsistent; it should do both, or neither. It's useful for us to be able to define SPs which may refer to tables AND/OR columns which don't exist in the schema yet, so is there a way to turn off SQL Server's checking of column existence in tables which currently exist?
This is called deferred name resolution.
There is no way of turning it off. You can use dynamic SQL or (a nasty hack!) add a reference to a non existent table so that compilation of that statement is deferred.
CREATE PROCEDURE [dbo].[MyProcedure]
AS
BEGIN
CREATE TABLE #Dummy (c int)
SELECT
NonExistantCol1, NonExistantCol2, NonExistantCol3
FROM
ExistantTable
WHERE NOT EXISTS(SELECT * FROM #Dummy)
DROP TABLE #Dummy
END
GO
This article in MSDN should answer your question.
From the article:
When a stored procedure is executed for the first time, the query
processor reads the text of the stored procedure from the
sys.sql_modules catalog view and checks that the names of the objects
used by the procedure are present. This process is called deferred
name resolution because table objects referenced by the stored
procedure need not exist when the stored procedure is created, but
only when it is executed.

SQL Server stored procedure meaning

I'm developing a simple database architecture in VisualParadigm and lately ran over next code excerpt.
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'getType') AND type in (N'P', N'PC'))
DROP PROCEDURE getType;
Next goes my stored procedure:
CREATE PROCEDURE getType #typeId int
AS
SELECT * FROM type t WHERE t.type_id = #typeId;
Can anyone explain what does it mean/do (the former one)?
P.S.: It would be great, if you may also check for any syntax errors as I'm totally new to SQL Server and stored procedures.
The IF EXISTS part first checks if a stored procedure with the same name exists. if it does it drops it before creating it. Without this check you'd get an error that the stored procedure already exists.
Adding to Raj's post, there is no means to do an "upsert" with stored procedures. The Create Procedure statement must be the first statement of the batch. Thus, the following would not work:
If Not Exists(Select 1 From sys.procedures Where Name = 'getType')
Create Procedure...
Else
Alter Procedure...
The only means to "update" a procedure and not have it throw an error if it already exists is to drop it and re-create it.
ADDITION
To address a specific question you made in comments, sys.objects is a catalog view which contains a list of all objects (tables, constraints, columns, indexes etc. Every "thing" in the database) of which procedures are one of them. Thus, this is checking whether the procedure object (based on filters on type) exist. The primary key of the sys.objects table/view is object_id which is an integer. In your example, they are using the OBJECT_ID function to find the id of the object getType and determine if it is a procedure. (It probably would have been safe to just use If OBJECT_ID(N'getType') is not null but just in case there is another object with that name that isn't a procedure, they added the check on the object type).
a CREATE PROCEDURE getType... will fail if the object already exists. by including the IF EXISTS... code which will drop the object if it exists first, you eliminate the error and the CREATE... will run.
The OBJECT_ID(N'getType') just returns the numeric ID of an object named N'getType' and the AND type in (N'P', N'PC')) makes sure that the object is a P=stored procedure or a PC=Assembly (CLR).
You could try using something like this, so you can use ALTER to keep permissions (DROP+CREATE removes any):
BEGIN TRY EXEC ('CREATE PROCEDURE YourProcedureName AS SELECT ''ERROR'' RETURN 999') END TRY BEGIN CATCH END CATCH
GO
ALTER PROCEDURE YourProcedureName
AS
SELECT 'WORKS!2'
GO
EXEC YourProcedureName
OUTPUT:
-------
WORKS!2
(1 row(s) affected)
It looks like this is part of a script to generate your DB. The first statement looks to see if your sproc called "getType" exists. If it does then it will drop it. Why? Because the next line is going to create it.
The only other way it could create it and make sure it matches the current version of your procedure is to change create to alter. That would make for longer code because it would have to list the sproc twice. Or it could generate dynamic sql which is not nearly as clean.
It's doing a drop and recreate
if a database object called getType exists:
WHERE object_id = OBJECT_ID(N'getType')
and it's a stored procedure:
AND type in (N'P', N'PC'))
then drop it before adding your stored procedure:
DROP PROCEDURE getType;
The first query drops procedure if it exists. The second creates a new procedure that takes integer parameter and returns resultset.