Создание дубликатов временных таблиц внутри вложенных хранимых процедур

ситуация такова:

процедура 1 создает временную таблицу (#MYTABLE) и вызывает процедуру 2. Процедура 2 также пытается создать #MYTABLE с разными столбцами. Когда процедура 2 пытается вставить данные в #MYTABLE, происходит ошибка с жалобой "недопустимое имя столбца". У меня есть два вопроса по этому поводу:

1) не должна ли система жаловаться, когда #MYTABLE создается внутри процедуры 2? Я понимаю, почему он не может возражать во время компиляции, но во время выполнения I ожидал бы ошибки.

2) учитывая, что он не жалуется на создание, и на самом деле, когда вы выбираете из #MYTABLE внутри процедуры 2, вы видите новый столбец, почему он жалуется на вставку?

ниже код. Удалив или инструкции Insert получите сообщение об ошибке.

(Я знаю много способов исправить эту ситуацию, поэтому мне не нужны ответы об этом. Я просто хочу понять, что происходит.)

IF OBJECT_ID(N'dbo.MYPROC1', N'P') IS NOT NULL
    DROP PROCEDURE dbo.MYPROC1;
GO

CREATE PROCEDURE dbo.MYPROC1
AS
    CREATE TABLE dbo.#MYTABLE ( Name VARCHAR(256) );

    SELECT
        'DO NOTHING 1' AS TABLENAME;

    EXEC dbo.MYPROC2;

GO

IF OBJECT_ID(N'dbo.MYPROC2', N'P') IS NOT NULL
    DROP PROCEDURE dbo.MYPROC2;
GO

CREATE PROCEDURE dbo.MYPROC2
AS
    SELECT
        'INSIDE PROC 2 BEFOREHAND' AS TABLENAME
       ,*
    FROM
        dbo.#MYTABLE;

    CREATE TABLE dbo.#MYTABLE
        (
         Name VARCHAR(256)
        ,LastName VARCHAR(256)
        );

    --INSERT  INTO dbo.#MYTABLE
    --        ( Name, LastName )
    --        SELECT
    --            'BARACK'
    --           ,'OBAMA';

    SELECT
        'INSIDE PROC 2 AFTERWARDS' AS TABLENAME
       ,*
    FROM
        dbo.#MYTABLE;

    --INSERT  INTO dbo.#MYTABLE
    --        ( Name, LastName )
    --        SELECT
    --            'BARACK'
    --           ,'OBAMA';

    SELECT
        'DO NOTHING 2' AS TABLENAME;

GO

EXEC MYPROC1;

4 ответов


с Создать Таблицу документы:

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


1) не должна ли система жаловаться, когда #MYTABLE создается внутри Процедура 2? Я понимаю, почему он не может возражать во время компиляции, но во время выполнения я ожидал бы ошибки.

это тут жаловаться во время компиляции. Когда он компилируется dbo.MYPROC2 Он видит, что таблица существует в родительской области и несовместима со списком столбцов, который вы используете. Если не было видимого родительского объекта с этим именем, то компиляция этого заявление было бы отложено до тех пор, пока оно не было выполнено (после CREATE TABLE).

если вы должны были удалить начальный SELECT С dbo.MYPROC2 и затем выполнить dbo.MYPROC2 прежде, чем dbo.MYPROC1 это, скорее всего, удастся - так как у него уже будет кэшированный план для dbo.MYPROC2 и нет необходимости перекомпилировать.

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


1) не должна ли система жаловаться, когда #MYTABLE создается внутри Процедура 2? Я понимаю, почему он не может возражать во время компиляции, но во время выполнения я ожидал бы ошибки.

нет, не должно. Вы получите 2 местные временные таблицы увидеть их имена:

CREATE PROCEDURE dbo.MYPROC1
AS
    CREATE TABLE dbo.#MYTABLE ( Name VARCHAR(256) );
    EXEC dbo.MYPROC2;
GO

CREATE PROCEDURE dbo.MYPROC2
AS
    CREATE TABLE dbo.#MYTABLE(
         Name VARCHAR(256)
        ,LastName VARCHAR(256));

    SELECT *
    FROM tempdb.INFORMATION_SCHEMA.TABLES
    WHERE [Table_name] LIKE '%MYTABLE%' 
GO

SqlFiddleDemo

выход:

╔════════════════╦═══════════════╦═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╦════════════╗
║ TABLE_CATALOG  ║ TABLE_SCHEMA  ║                                                            TABLE_NAME                                                             ║ TABLE_TYPE ║
╠════════════════╬═══════════════╬═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╬════════════╣
║ tempdb         ║ dbo           ║ #MYTABLE____________________________________________________________________________________________________________000000000117  ║ BASE TABLE ║
║ tempdb         ║ dbo           ║ #MYTABLE____________________________________________________________________________________________________________000000000118  ║ BASE TABLE ║
╚════════════════╩═══════════════╩═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╩════════════╝

2) учитывая, что он не жалуется на творение, и в самом деле, когда вы выбираете из #MYTABLE внутри процедуры 2, вы видите новый столбец, почему он жалуется на вставку?

поскольку SQL Server получает первое определение таблицы из внешней хранимой процедуры. Он имеет разные столбцы, поэтому вы получите ошибку во время INSERT


ну, на первый взгляд, ваше предположение хорошо, но только на первый.

при создании временной таблицы с именем MyTable SQL Server создает фактическую таблицу в TEMPDB, которая называется чем-то вроде " MyTable_____________...._____01D', поэтому, когда любой другой фрагмент кода создает таблицу с тем же именем, но в другой области, сервер может изменить их.

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

Я бы предложил вам выбрать данные из sys.объекты, так что вы можете видеть, что есть две фактические и разные таблицы, созданные -select name from tempdb..sysobjects where name like 'MYTABLE%'

и last-вы используете то же имя и ожидаете получить доступ к "самой маленькой" таблице области, но на самом деле сервер использует таблицу, которая была создана первой. Предположим, что SQL server просто выбирает top 1 из системный.объекты, где область и имя совпадают с текущими.