Question

I think there's something I'm missing when it comes to which database is being used when running a custom system stored procedure. I have the following in my stored procedure (edited for brevity):

ALTER PROCEDURE sp_mysp
AS
IF (EXISTS (
    SELECT *
    FROM INFORMATION_SCHEMA.TABLES
    WHERE TABLE_NAME = 'mytable'))
BEGIN
    --Do stuff to the table
END
ELSE
BEGIN
    PRINT 'Table mytable does not exist'
END

The problem is that when I call the procedure from mydb, the Master table is used for the initial check, rather than the current database. If I write mydb.INFORMATION_SCHEMA.TABLES it works, but that's not really a solution, as it's defeating the whole point of maintaining this as a single SP rather than one SP for each DB.

Any ideas? I tried passing the DB name as a parameter and starting the SP with 'use @db_name', but apparently stored procedures don't allow use statements.

Thanks in advance for all help.

Was it helpful?

Solution

You need to mark your stored procedure as a system object to get the behaviour that you want (full example below tested as working on 2008 SP3)

USE master;
GO

CREATE PROCEDURE dbo.sp_mysp
AS
SELECT *
FROM INFORMATION_SCHEMA.TABLES

GO

USE tempdb;
EXEC dbo.sp_mysp /*Returns tables from master*/

GO

USE master;
EXEC sys.sp_MS_marksystemobject sp_mysp

GO

USE tempdb;
EXEC dbo.sp_mysp /*Returns tables from tempdb*/

This is an undocumented approach and may not work in future versions.

OTHER TIPS

Alternative: using dynamic SQL. And no, in controlled admin functionality, dynamic SQL is not the evil thing it's often made out to be.

USE master;
GO
CREATE PROCEDURE dbo.sp_find_table
    @db_name    SYSNAME,
    @table_name SYSNAME
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @sql NVARCHAR(MAX);

    SELECT @sql = 'SELECT QUOTENAME(s.name)
      + ''.'' + QUOTENAME(t.name)
      FROM ' + @db_name + '.sys.tables AS t
      INNER JOIN ' + @db_name + '.sys.schemas AS s
      ON t.[schema_id] = s.[schema_id];';

    EXEC sp_executesql @sql;
END
GO

Now your call from any other database can be:

DECLARE @db SYSNAME = DB_NAME(DB_ID());
EXEC master.dbo.sp_find_table @db_name = @db, @table_name = 'floob';

Or simply:

EXEC master.dbo.sp_find_table 'tempdb', @table_name = 'floob';

You can even create a synonym in model (and all existing databases) like follows:

CREATE SYNONYM dbo.sp_find_table FOR master.dbo.sp_find_table;
GO

(Now you can take the prefix off of the above calls.)

While your call is one step more complex, the benefits of this approach:

  • you can put this procedure in any utility database you want, instead of mucking up master with your procedures. Dependence on non-system objects in master can be an issue if you migrate to a new server, for example.
  • you don't rely on undocumented, unsupported, and not-guaranteed-to-be-forward-compatible functionality.
  • you can call it for any database, from any database, without relying on the current database context.
Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top