Может ли хранимая процедура ссылаться на базу данных, в которой она хранится?

8

Предположим, у меня есть хранимая процедура, которая с некоторыми изменениями дублируется в нескольких базах данных. И я хочу сослаться на базу данных, в которой хранится хранимая процедура, даже если она выполняется в другой базе данных.

Есть ли способ получить полный путь (..) или иным образом получить базу данных, в которой хранится хранимая процедура, а не текущую базу данных?

Джим Кларк
источник
Я не знал, что процесс выполняется в контексте базы данных, в которой он находится, и я хочу убедиться, что запуск его в другой базе данных не повлияет на базу данных, в которой вы были.
Джим Кларк

Ответы:

13

Я хочу сослаться на базу данных, в которой хранится хранимая процедура, даже если она выполняется [из] другой базы данных.

Просто используйте одночастные или двухкомпонентные имена в хранимой процедуре, и она будет ссылаться на объекты в базе данных, содержащей хранимую процедуру. В частности,

Для статического SQL в хранимой процедуре:

  • Неквалифицированные имена объектов разрешаются относительно схемы, содержащей хранимую процедуру.

  • Имена из двух частей будут сопоставляться с базой данных, содержащей хранимую процедуру.

Для динамического SQL в хранимой процедуре:

  • Неквалифицированные имена объектов разрешаются относительно схемы по умолчанию идентификатора пользователя, выполняющего хранимую процедуру (по умолчанию вызывающая сторона).

  • Имена из двух частей будут сопоставляться с базой данных, содержащей хранимую процедуру.

Функция db_name () будет возвращать имя базы данных, содержащей хранимую процедуру, в обоих случаях.

Дэвид Браун - Microsoft
источник
4

Вот небольшой пример, который я собрал, показывающий общие функции, используемые для того, чтобы приблизиться к тому, что вы ищете.

/** Create a procedure in master to demonstrate
    DB_NAME()
    OBJECT_SCHEMA_NAME()
    OBJECT_NAME()
    @@PROCID
**/
USE [master]
GO

CREATE OR ALTER PROCEDURE dbo.uspTestMe
AS
BEGIN

    PRINT 'Database: ' + DB_NAME()
    PRINT 'Schema Name: ' + OBJECT_SCHEMA_NAME(@@PROCID)
    PRINT 'Procedure Name: ' + OBJECT_NAME(@@PROCID)

END

GO

/** CHANGE Context to TempDB
    Execute procedure in master
    **/
USE [tempdb]
GO

EXEC master.dbo.uspTestMe 

GO

/** Cleanup in master **/
USE [master]
GO

DROP PROCEDURE IF EXISTS dbo.uspTestMe 
Джонатан Файт
источник
0

Добавление к отличному ответу Дэвида Брауна :

Я хочу сослаться на базу данных, в которой хранится хранимая процедура, даже если она выполняется в другой базе данных.

Вам повезло, потому что именно так обычные / постоянные несистемные хранимые процедуры уже работают таким образом, и вам действительно нужно приложить все усилия, чтобы заставить объекты работать в текущей БД. Так работают функции, просто у хранимых процедур у вас есть опции - помечать их как «системные хранимые процедуры» или создавать временные хранимые процедуры - чего у вас нет с функциями / представлениями / триггерами / и т. Д.

Поскольку встроенные функции ведут себя несколько иначе, чем статический SQL во временных хранимых процедурах, в следующем примере используется временная таблица для ссылки как в UDF, так и в Inline-TVF. Правда, следующий пример на самом деле не тестирует временные хранимые процедуры, но причина, по которой я использовал невременную таблицу, заключается в том, что, поскольку у нас есть один экземпляр отличающегося поведения, мы должны убедиться, что такого поведения здесь не происходит. В следующем примере, если какой-либо тип функции знал о «текущей» базе данных, то ссылка на временную таблицу вызвала бы ошибку.

НАСТРОИТЬ

USE [tempdb];

CREATE IF NOT EXISTS TABLE dbo.InTempDB (Col1 INT);
INSERT INTO dbo.InTempDB ([Col1]) VALUES (999);

GO
CREATE 
OR ALTER -- comment out if using SQL Server < 2017
FUNCTION dbo.GetDbNameUDF()
RETURNS SYSNAME
AS
BEGIN
  DECLARE @DoNothing INT;
  SELECT @DoNothing = [Col1] FROM dbo.InTempDB;

  RETURN DB_NAME();
END;

GO
CREATE
OR ALTER -- comment out if using SQL Server < 2017
FUNCTION dbo.GetDbNameITVF()
RETURNS TABLE
AS
RETURN
  SELECT DB_NAME() AS [DbName],
         tmp.[Col1]
  FROM   dbo.InTempDB tmp;

GO

ТЕСТОВОЕ ЗАДАНИЕ

USE [model];

SELECT DB_NAME() AS [CurrentDB],
       tempdb.dbo.GetDbNameUDF() AS [DbNameFromUDF];
-- CurrentDB    DbNameFromUDF
-- model        tempdb


SELECT DB_NAME() AS [CurrentDB],
       *
FROM   tempdb.dbo.GetDbNameITVF();
-- CurrentDB    DbName    Col1
-- model        tempdb    999


/* -- clean-up
DROP TABLE dbo.InTempDB;
DROP FUNCTION dbo.GetDbNameUDF;
DROP FUNCTION dbo.GetDbNameITVF;
*/
Соломон Руцкий
источник