Análisis de la extraña imagen de la tabla temporal con el mismo nombre en el procedimiento almacenado anidado de SQL Server

En los procedimientos almacenados anidados de SQL Server, ¿puede haber tablas temporales locales con el mismo nombre en los procedimientos almacenados externos y los procedimientos almacenados internos (procedimientos almacenados que se llaman por anidación)? Si es así, ¿hay algún problema o restricción? En el procedimiento almacenado anidado, ¿es la tabla temporal del procedimiento almacenado externo o la tabla temporal definida por uno mismo? ¿Existe un alcance de "alcance" para las tablas temporales locales, similar a las variables en los lenguajes de alto nivel?

Nota: También se puede llamar el procedimiento almacenado principal y el procedimiento almacenado secundario, el procedimiento almacenado externo y el procedimiento almacenado interno. Estos son solo títulos o nombres diferentes. Aquí utilizamos de manera uniforme procedimientos almacenados externos y procedimientos almacenados internos. No entraré en el artículo de seguimiento.

Primero veamos un ejemplo, como se muestra a continuación, construimos un ejemplo simple.

SI EXISTE (SELECCIONAR 1 DE sys.objects DONDE object_id = OBJECT_ID (N'dbo.PRC_TEST ') y OBJECTPROPERTY (object_id, 'IsProcedure') = 1)
COMENZAR
DROP PROCEDIMIENTO dbo.PRC_TEST
FIN
GO
CREATE PROC dbo.PRC_TEST
AS
COMENZAR

CREATE TABLE #tmp_test (id INT);

INSERT INTO #tmp_test
SELECT 1;

SELECCIONAR * DE #tmp_test;

EXEC PRC_SUB_TEST

SELECT * FROM #tmp_test


END
GO

IF EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID (N'dbo.PRC_SUB_TEST ') Y OBJECTPROPERTY (object_id,' IsProcedure ') = 1)
BEGIN
DROPESTCEDURE;
FIN
VAYA

CREAR PROCEDIMIENTO dbo.PRC_SUB_TEST
AS
COMENZAR

CREATE #tmp_test TABLA (nombre VARCHAR (128));

INSERT INTO #tmp_test
SELECCIONAR nombre DE sys.objects

SELECT * FROM #tmp_test;
END
GO

EXEC PRC_TEST;

La prueba simple pareció normal y no se encontraron problemas. Si saca una conclusión en este momento, ¡es demasiado pronto! Por ejemplo, si ves que un cisne es blanco, si llegas a una conclusión: "Todos los cisnes son blancos", de hecho, hay cisnes negros en este mundo, ¡pero nunca lo has visto! Como se muestra a continuación, modificamos el procedimiento almacenado dbo.PRC_SUB_TEST, reemplazamos * con el nombre del campo, como se muestra a continuación:

SI EXISTE (SELECCIONAR 1 DE sys.objects DONDE object_id = OBJECT_ID (N'dbo.PRC_SUB_TEST') y OBJECTPROPERTY (object_id, 'IsProcedure') = 1)
COMIENZO
DROP PROCEDIMIENTO dbo.PRC_SUB_TEST;
END
GO

CREAR PROCEDIMIENTO dbo.PRC_SUB_TEST
AS
BEGIN

CREATE TABLE #tmp_test (nombre VARCHAR (128));

INSERT INTO #tmp_test
SELECCIONAR nombre DE sys.objects

SELECCIONAR nombre DE #tmp_test;
FIN
IR

Luego repita la prueba anterior, como se muestra a continuación, si el procedimiento almacenado dbo.PRC_TEST se ejecuta en este momento, se informará un error: "Nombre de columna no válido 'nombre'".

En este punto, siempre que ejecute el procedimiento almacenado dbo.PRC_SUB_TEST una vez y luego ejecute el procedimiento almacenado dbo.PRC_TEST, no se informará ningún error. Y siempre que ejecute este procedimiento almacenado una vez y luego ejecute dbo.PRC_TEST en la sesión actual o en cualquier otra sesión, no se informará ningún error. ¿Es muy confuso o confuso?

EXEC dbo.PRC_SUB_TEST;

EXEC PRC_TEST;

Si desea reproducir este fenómeno nuevamente, solo puede reproducir este fenómeno a través del siguiente SQL o eliminar / reconstruir procedimientos almacenados. Parece un pequeño fenómeno fantasma.

PREOCAJE LIBRE DBCC

Con respecto a este fenómeno, el documento oficial (ver la dirección del enlace del material de referencia para más detalles) tiene tal descripción:

Una tabla temporal local creada dentro de un procedimiento almacenado o desencadenador puede tener el mismo nombre que una tabla temporal que se creó antes de que se llamara al procedimiento almacenado o desencadenador. Sin embargo, si una consulta hace referencia a una tabla temporal y existen dos tablas temporales con el mismo nombre en ese momento, no se define en qué tabla se resuelve la consulta. Los procedimientos almacenados anidados también pueden crear tablas temporales con el mismo nombre que una tabla temporal creada por el procedimiento almacenado que la llamó. Sin embargo, para que las modificaciones se resuelvan en la tabla que se creó en el procedimiento anidado, la tabla debe tener la misma estructura, con los mismos nombres de columna, que la tabla creada en el procedimiento de llamada. Esto se muestra en el siguiente ejemplo.

El nombre de la tabla temporal local creada en el procedimiento almacenado o desencadenador puede ser el mismo que el nombre de la tabla temporal creada antes de llamar al procedimiento almacenado o desencadenador. Sin embargo, si la consulta hace referencia a una tabla temporal y hay dos tablas temporales con el mismo nombre al mismo tiempo, no define con qué tabla se analiza la consulta. Un procedimiento almacenado anidado también puede crear una tabla temporal con el mismo nombre que la tabla temporal creada por el procedimiento almacenado que lo llama. Sin embargo, para modificarlo para que se resuelva en una tabla creada en un procedimiento anidado, esta tabla debe tener la misma estructura y nombres de columna que la tabla creada por el procedimiento de llamada. El siguiente ejemplo ilustra este punto.

CREAR PROCEDIMIENTO dbo.Test2
COMO
CREAR TABLA #t (x INT PRIMARY KEY);
INSERTAR EN #t VALORES (2);
SELECCIONE Test2Col = x FROM #t;
IR A

CREAR PROCEDIMIENTO dbo.Test1
COMO
CREAR TABLA #t (x INT PRIMARY KEY);
INSERTAR EN #t VALORES (1);
SELECCIONE Test1Col = x FROM #t;
EXEC Test2;
IR A

CREAR TABLA #t (x INT PRIMARY KEY);
INSERTAR EN #t VALORES (99);
GO

EXEC Test1;
VAMOS

En el documento oficial, "Hay dos tablas temporales con el mismo nombre al mismo tiempo, entonces no se define con qué tabla analizar la consulta", la explicación se siente un poco confusa. Aquí hay una breve explicación, en la llamada anidada del procedimiento almacenado, se permite tener una tabla temporal local con el mismo nombre en el procedimiento externo y el procedimiento almacenado interno, pero en el proceso de memoria, si desea modificar o analizarlo (la modificación es buena Comprender, como agregar índices, agregar campos y otras operaciones DDL; para analizar, consultar tablas temporales, especificar nombres de campo en SQL, debe resolver resolver), entonces esta tabla temporal debe tener la misma estructura de tabla en este momento, de lo contrario, se informará un error. El documento oficial le dice que no es posible, pero no se indica el motivo específico. Entonces, también podríamos hacer algunas especulaciones, si se crean dos tablas temporales locales en la llamada anidada del procedimiento almacenado. ¿Es posible que solo se cree realmente una tabla temporal local? ¿Qué pasa con la reutilización de tablas temporales locales? Luego, simplemente verificamos, como se muestra a continuación, aquí puede juzgar que en realidad se crean dos tablas temporales locales. No hay reutilización de tablas temporales.

SELECCIONE *
FROM sys.dm_os_performance_counters
DONDE counter_name LIKE 'Tasa de creación de tablas temporales%';

EXEC PRC_TEST;

SELECCIONE *
FROM sys.dm_os_performance_counters
DONDE counter_name LIKE 'Tasa de creación de tablas temporales%';

 

Por supuesto, puede usar el siguiente SQL para verificar, que es consistente con los resultados de la verificación anterior.

SI EXISTE (SELECCIONAR 1 DE sys.objects DONDE object_id = OBJECT_ID (N'dbo.PRC_SUB_TEST') y OBJECTPROPERTY (object_id, 'IsProcedure') = 1)
COMIENZO
DROP PROCEDIMIENTO dbo.PRC_SUB_TEST;
END
GO


CREAR PROCEDIMIENTO dbo.PRC_SUB_TEST
AS
BEGIN

SELECT * FROM #tmp_test;

SELECCIONAR * DE tempdb.dbo.sysobjects DONDE nombre LIKE '# tmp_test%'
CREAR TABLA #tmp_test (nombre VARCHAR (128));

INSERT INTO #tmp_test
SELECCIONAR nombre DE sys.objects
SELECT * FROM tempdb.dbo.sysobjects DONDE nombre LIKE '# tmp_test%'
SELECT * FROM #tmp_test;
FIN
IR

Entonces, echemos un vistazo al "alcance" de la tabla temporal. Lamento haber usado ese concepto. El documento oficial no tiene este concepto. Este es solo un aspecto de nuestro pensamiento, y no hay necesidad de levantar hasta los detalles. Como se muestra a continuación, modificamos el procedimiento almacenado

SI EXISTE (SELECCIONAR 1 DE sys.objects DONDE object_id = OBJECT_ID (N'dbo.PRC_SUB_TEST') y OBJECTPROPERTY (object_id, 'IsProcedure') = 1)
COMIENZO
DROP PROCEDIMIENTO dbo.PRC_SUB_TEST;
END
GO
CREAR PROCEDIMIENTO dbo.PRC_SUB_TEST
AS
BEGIN

SELECT * FROM #tmp_test;
CREAR TABLA #tmp_test (nombre VARCHAR (128));

INSERT INTO #tmp_test
SELECCIONAR nombre DE sys.objects

SELECT * FROM #tmp_test;
FIN
IR

A través de la verificación experimental, descubrimos que la tabla temporal del procedimiento almacenado externo es efectiva en el procedimiento almacenado interno. Su "alcance" es anterior a la creación de la tabla temporal del mismo nombre en el procedimiento almacenado interno. Es lo mismo que el variables globales y locales en el lenguaje de alto nivel El alcance de la variable es un poco similar.

Dado que se han creado dos tablas temporales locales, ¿por qué se informa de un error al modificar o analizar? Una suposición personal es que después de que el optimizador lo ha analizado, durante el proceso de ejecución, el motor de la base de datos no puede juzgarlo o no existe tal lógica en el código para controlar qué tabla temporal se recupera durante el análisis o modificación. Puede ser un defecto en el código o alguna razón lógica. Lo anterior es solo una suposición y un razonamiento personal. Si hay alguna deficiencia o error, corríjame.

Supongo que te gusta

Origin blog.csdn.net/hbznd/article/details/115247701
Recomendado
Clasificación