¿Hay alguna forma de conservar una variable en un proceso?

83

¿Hay alguna forma de conservar una variable en un proceso?

Declare @bob as varchar(50);
Set @bob = 'SweetDB'; 
GO
USE @bob  --- see note below
GO
INSERT INTO @bob.[dbo].[ProjectVersion] ([DB_Name], [Script]) VALUES (@bob,'1.2')

Vea esta pregunta SO para la línea 'USE @bob'.

NitroxDM
fuente
¿Por qué necesita calificar el nombre de la tabla con el nombre de la base de datos? Supongo que se hizo una pregunta similar antes de esta.
shahkalpesh
Y no hay forma de calificar los nombres de las tablas con el nombre de la base de datos en una variable como esa. Con su pregunta anterior sobre el uso de una variable con la declaración USE, supongo que tendrá que hacer todo en SQL dinámico, con todo el dolor que arrastra a la mesa.
Lasse V. Karlsen
El script actual integra 4 bases de datos diferentes. He comentado instrucciones para buscar y reemplazar dbName1, dbName2, dbName3 y dbName4. Solo pensé que sería menos propenso a errores que el cliente simplemente estableciera cuatro variables.
NitroxDM
El título de la pregunta es una pregunta realmente importante, pero el código de ejemplo es terrible. Como muestra la respuesta aceptada, no necesita "ir" en su ejemplo. El resultado es que la respuesta aceptada no responde a la pregunta en su título.
Greg Woods

Respuestas:

31

El gocomando se usa para dividir el código en lotes separados. Si eso es exactamente lo que quiere hacer, entonces debería usarlo, pero significa que los lotes están realmente separados y no puede compartir variables entre ellos.

En su caso, la solución es sencilla; simplemente puede eliminar las godeclaraciones, no son necesarias en ese código.

Nota al margen: no puede usar una variable en una usedeclaración, tiene que ser el nombre de una base de datos.

Guffa
fuente
1
Algunas sentencias SQL deben ser la primera sentencia de un bloque (la región entre sentencias GO). Por ejemplo: CREATE PROCEDURE o CREATE FUNCTION deben ocurrir antes de cualquier otra declaración, ya sea en la parte superior del script o inmediatamente después de la declaración GO (nota: se permiten espacios en blanco y comentarios antes de estas declaraciones). Cuando se ejecutan scripts donde tales declaraciones deben ocurrir después de otra lógica, se requieren las declaraciones GO. Pero debo estar de acuerdo en que, en la mayoría de los casos, las declaraciones GO se pueden eliminar.
Zarepheth
@Zarepheth: Buen punto. No es necesario en este código específico, pero es útil saber que pueden ser necesarios en algunos casos.
Guffa
1
¿Por qué el voto negativo? Si no explica qué es lo que cree que está mal, no podrá mejorar la respuesta.
Guffa
2
@jwize: No, no es necesario separarlos, eso se puede hacer en el mismo bloque.
Guffa
1
@Ben: el gocomando se usa para dividir el código en lotes separados. Si eso es lo que quiere hacer, entonces debería usarlo, pero significa que los lotes están realmente separados y no puede compartir variables entre ellos.
Guffa
128

Utilice una mesa temporal:

CREATE TABLE #variables
    (
    VarName VARCHAR(20) PRIMARY KEY,
    Value VARCHAR(255)
    )
GO

Insert into #variables Select 'Bob', 'SweetDB'
GO

Select Value From #variables Where VarName = 'Bob'
GO

DROP TABLE #variables
go
RBarryYoung
fuente
13
gran respuesta ... realmente RESPONDIÓ a la pregunta PREGUNTADA en lugar de dar una solución.
Cos Callis
1
Esta es la respuesta correcta. Buena solucion. Además, es bueno que, si usa una gran cantidad de variables, estén todas en una tabla de fácil acceso, sin desplazarse hacia arriba y hacia abajo en el SP buscando sus declaraciones.
ColinMac
15

Prefiero esta respuesta de esta pregunta Variables globales con GO

Lo cual tiene el beneficio adicional de poder hacer lo que originalmente quería hacer también.

La advertencia es que debe activar el modo SQLCMD (en Consulta-> SQLCMD) o activarlo de forma predeterminada para todas las ventanas de consulta (Herramientas-> Opciones y luego Resultados de la consulta-> Por defecto, abrir nuevas consultas en modo SQLCMD)

Luego puede usar el siguiente tipo de código (completamente extraído de la misma respuesta por Oscar E. Fraxedas Tormo )

--Declare the variable
:setvar MYDATABASE master
--Use the variable
USE $(MYDATABASE);
SELECT * FROM [dbo].[refresh_indexes]
GO
--Use again after a GO
SELECT * from $(MYDATABASE).[dbo].[refresh_indexes];
GO
Matt Vukomanovic
fuente
Estaba redirigiendo la salida de la consulta a otro archivo (: out filename) en modo SQLCMD, y para que la salida se vacíe en el archivo, debe ejecutar un GO, por lo que esto: la sintaxis setvar es necesaria para reemplazar las variables normales en esa situación ya que ' se ven obligados a dividir las cosas en lotes.
Anssssss
¡Excelente! ¡Esto debería estar marcado como la verdadera respuesta correcta!
SQL Police
3

Si está utilizando SQL Server, puede configurar variables globales para scripts completos como:

:setvar sourceDB "lalalallalal"

y usar más adelante en el script como:

$(sourceDB)

Asegúrese de que el modo SQLCMD esté activado en Server Managment Studi, puede hacerlo a través del menú superior Haga clic en Consulta y active el modo SQLCMD.

Puede encontrar más información sobre el tema aquí: Documentación de MS

DanteTheSmith
fuente
1

No estoy seguro si esto ayuda

declare @s varchar(50)
set @s='Northwind'

declare @t nvarchar(100)
set @t = 'select * from ' + @s + '.[dbo].[Customers]'

execute sp_executesql @t
Shahkalpesh
fuente
1

Las tablas temporales se conservan sobre las declaraciones GO, así que ...

SELECT 'value1' as variable1, 'mydatabasename' as DbName INTO #TMP

-- get a variable from the temp table
DECLARE @dbName VARCHAR(10) = (select top 1 #TMP.DbName from #TMP)
EXEC ('USE ' + @dbName)
GO

-- get another variable from the temp table
DECLARE @value1 VARCHAR(10) = (select top 1 #TMP.variable1 from #TMP)

DROP TABLE #TMP

No es bonito, pero funciona

Remco Nonhebel
fuente
1

Cree sus propios procedimientos almacenados que se guardan / cargan en una tabla temporal.

MyVariableSave   -- Saves variable to temporary table. 
MyVariableLoad   -- Loads variable from temporary table.

Entonces puedes usar esto:

print('Test stored procedures for load/save of variables across GO statements:')

declare @MyVariable int = 42
exec dbo.MyVariableSave @Name = 'test', @Value=@MyVariable
print('  - Set @MyVariable = ' + CAST(@MyVariable AS VARCHAR(100)))

print('  - GO statement resets all variables')
GO -- This resets all variables including @MyVariable

declare @MyVariable int
exec dbo.MyVariableLoad 'test', @MyVariable output
print('  - Get @MyVariable = ' + CAST(@MyVariable AS VARCHAR(100)))

Salida:

Test stored procedures for load/save of variables across GO statements:
  - Set @MyVariable = 42
  - GO statement resets all variables
  - Get @MyVariable = 42

También puede utilizar estos:

exec dbo.MyVariableList       -- Lists all variables in the temporary table.
exec dbo.MyVariableDeleteAll  -- Deletes all variables in the temporary table.

Salida de exec dbo.MyVariableList:

Name    Value
test    42

Resulta que poder enumerar todas las variables en una tabla es bastante útil. Entonces, incluso si no carga una variable más tarde, es excelente para fines de depuración ver todo en un solo lugar.

Esto usa una tabla temporal con un ##prefijo, por lo que es suficiente para sobrevivir a una instrucción GO. Está destinado a utilizarse dentro de un único script.

Y los procedimientos almacenados:

-- Stored procedure to save a variable to a temp table.
CREATE OR ALTER PROCEDURE MyVariableSave 
    @Name varchar(255),
    @Value varchar(MAX)
WITH EXECUTE AS CALLER
AS  
BEGIN
    SET NOCOUNT ON
    IF NOT EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
    BEGIN
        DROP TABLE IF EXISTS ##VariableLoadSave
        CREATE TABLE ##VariableLoadSave
        (
            Name varchar(255),
            Value varchar(MAX)
        )
    END
    UPDATE ##VariableLoadSave SET Value=@Value WHERE Name=@Name
    IF @@ROWCOUNT = 0
        INSERT INTO ##VariableLoadSave SELECT @Name, @Value
END
GO
-- Stored procedure to load a variable from a temp table.
CREATE OR ALTER PROCEDURE MyVariableLoad 
    @Name varchar(255),
    @Value varchar(MAX) OUT
WITH EXECUTE AS CALLER
AS  
BEGIN
    IF EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
    BEGIN
        IF NOT EXISTS(SELECT TOP 1 * FROM ##VariableLoadSave WHERE Name=@Name)
        BEGIN
            declare @ErrorMessage1 as varchar(200) = 'Error: cannot find saved variable to load: ' + @Name
            raiserror(@ErrorMessage1, 20, -1) with log
        END

        SELECT @Value=CAST(Value AS varchar(MAX)) FROM ##VariableLoadSave
        WHERE Name=@Name
    END
    ELSE
    BEGIN
        declare @ErrorMessage2 as varchar(200) = 'Error: cannot find saved variable to load: ' + @Name
        raiserror(@ErrorMessage2, 20, -1) with log
    END
END
GO
-- Stored procedure to list all saved variables.
CREATE OR ALTER PROCEDURE MyVariableList
WITH EXECUTE AS CALLER
AS  
BEGIN
    IF EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
    BEGIN
        SELECT * FROM ##VariableLoadSave
        ORDER BY Name
    END
END
GO
-- Stored procedure to delete all saved variables.
CREATE OR ALTER PROCEDURE MyVariableDeleteAll
WITH EXECUTE AS CALLER
AS  
BEGIN
    DROP TABLE IF EXISTS ##VariableLoadSave
    CREATE TABLE ##VariableLoadSave
    (
        Name varchar(255),
        Value varchar(MAX)
    )
END
Aplazamiento de pago
fuente
0

Si solo necesita un sí / no binario (como si existiera una columna), puede usarlo SET NOEXEC ONpara deshabilitar la ejecución de declaraciones. SET NOEXEC ONfunciona en GO (en lotes). Pero recuerde que debe dar marcha atrás en EXEC con SET NOEXEC OFFal final de la secuencia de comandos.

IF COL_LENGTH('StuffTable', 'EnableGA') IS NOT NULL
    SET NOEXEC ON -- script will not do anything when column already exists

ALTER TABLE dbo.StuffTable ADD EnableGA BIT NOT NULL CONSTRAINT DF_StuffTable_EnableGA DEFAULT(0)
ALTER TABLE dbo.StuffTable SET (LOCK_ESCALATION = TABLE)
GO
UPDATE dbo.StuffTable SET EnableGA = 1 WHERE StuffUrl IS NOT NULL
GO
SET NOEXEC OFF

Esto compila declaraciones pero no las ejecuta. De modo que seguirá recibiendo "errores de compilación" si hace referencia a un esquema que no existe. Por lo tanto, funciona para "apagar" el script en la segunda ejecución (lo que estoy haciendo), pero no funciona para desactivar partes del script en la primera ejecución, porque todavía obtendrá errores de compilación si hace referencia a columnas o tablas que no funcionan. todavía no existe.

yzorg
fuente
0

Puede hacer uso de NOEXEC siguiendo los pasos a continuación:

Crear mesa

#temp_procedure_version(procedure_version varchar(5),pointer varchar(20))

inserte versiones de procedimiento y puntero a la versión en una tabla temporal #temp_procedure_version

--ejemplo puntero de versión_procedimiento

insertar en temp_procedure_versionvalores (1.0, 'primera versión')

insertar en temp_procedure_versionvalores (2.0, 'versión final')

luego recupere la versión del procedimiento, puede usar la condición where como en la siguiente declaración

Seleccionar @ProcedureVersion=ProcedureVersionde #temp_procedure_versiondonde pointer='first version'

IF (@ProcedureVersion='1.0')
    BEGIN
    SET NOEXEC OFF  --code execution on 
    END
ELSE
    BEGIN 
    SET NOEXEC ON  --code execution off
    END 

--insertar el procedimiento versión 1.0 aquí

Cree el procedimiento versión 1.0 como .....

SET NOEXEC OFF -- execution is ON

Seleccione @ProcedureVersion=ProcedureVersiondesde #temp_procedure_versiondonde pointer = 'versión final'

IF (@ProcedureVersion='2.0')
    BEGIN
    SET NOEXEC OFF  --code execution on 
    END
ELSE
    BEGIN 
    SET NOEXEC ON  --code execution off
    END 

Cree el procedimiento versión 2.0 como .....

SET NOEXEC OFF -- execution is ON

- suelta la tabla temporal

Mesa plegable #temp_procedure_version

Viv Pathania
fuente