Есть ли способ сохранить переменную на ходу?

84

Есть ли способ сохранить переменную на ходу?

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')

См. Этот вопрос SO для строки «USE @bob».

NitroxDM
источник
Зачем нужно уточнять имя таблицы именем БД? Думаю, перед этим задавали аналогичный вопрос.
Shahkalpesh 01
И нет возможности квалифицировать имена таблиц с именем базы данных в такой переменной. Отвечая на его предыдущий вопрос об использовании переменной с оператором USE, я предполагаю, что ему нужно будет делать все в динамическом SQL, со всей болью, которая переносится на таблицу.
Лассе В. Карлсен
Фактический скрипт объединяет 4 разные базы данных. Я прокомментировал инструкции по поиску и замене dbName1, dbName2, dbName3 и dbName4. Я просто подумал, что для клиента будет меньше ошибок, если просто установить четыре переменные.
NitroxDM 02
Заголовок вопроса - действительно важный вопрос, но код примера ужасен. Как показывает принятый ответ, в вашем примере вам не нужно было идти. В результате принятый ответ не отвечает на вопрос в вашем заголовке.
Грег Вудс

Ответы:

31

В go используется для разделения кода на отдельные пакеты. Если это именно то, что вы хотите сделать, вам следует использовать его, но это означает, что пакеты фактически разделены, и вы не можете совместно использовать переменные между ними.

В вашем случае решение простое; вы можете просто удалить goоператоры, они не нужны в этом коде.

Боковое примечание: вы не можете использовать переменную в useинструкции, это должно быть имя базы данных.

Гуффа
источник
1
Некоторые операторы SQL должны быть первым оператором в блоке (область между операторами GO). Например: CREATE PROCEDURE или CREATE FUNCTION должны выполняться перед любыми другими операторами - либо в верхней части скрипта, либо сразу после оператора GO (примечание: перед этими операторами разрешены пробелы и комментарии). При запуске сценариев, в которых такие операторы должны появляться после другой логики, требуются операторы GO. Но я должен согласиться, что в большинстве случаев операторы GO можно удалить.
Зарепет
@Zarepheth: Хорошая мысль. В этом конкретном коде это не нужно, но полезно знать, что в некоторых случаях они могут понадобиться.
Guffa
1
Почему голос против? Если вы не объясните, что вы считаете неправильным, это не улучшит ответ.
Guffa
2
@jwize: Нет, вам не нужно их разделять, это можно сделать в одном блоке.
Guffa
1
@Ben: goкоманда используется для разделения кода на отдельные пакеты. Если это то, что вы хотите сделать, вам следует использовать это, но это означает, что пакеты фактически разделены, и вы не можете совместно использовать переменные между ними.
Guffa
131

Используйте временную таблицу:

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
источник
13
отличный ответ ... вы на самом деле ОТВЕТИЛИ на ЗАДАННЫЙ вопрос, а не пытаетесь обойти его.
Cos Callis
1
Это правильный ответ. Хорошее решение. Кроме того, приятно, что если вы используете большое количество переменных, все они находятся в одной легко доступной таблице, без прокрутки вверх и вниз по SP в поисках ваших объявлений.
ColinMac
15

Я предпочитаю этот ответ от этого вопроса Глобальные переменные с GO

Который имеет дополнительное преимущество в том, что вы можете делать то, что вы изначально хотели делать.

Предостережение заключается в том, что вам нужно включить режим SQLCMD (в разделе Query-> SQLCMD) или включить его по умолчанию для всех окон запросов (Инструменты-> Параметры, затем Результаты запроса-> По умолчанию открывать новые запросы в режиме SQLCMD)

Затем вы можете использовать следующий тип кода (полностью скопированный из того же ответа Оскара Э. Фракседаса Тормо )

--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
Мэтт Вукоманович
источник
Я перенаправлял вывод запроса в другой файл (: out filename) в режиме SQLCMD, и чтобы получить вывод, сброшенный в файл, вы должны выполнить GO, поэтому синтаксис: setvar необходим для замены обычных переменных в этой ситуации, поскольку вы ' вынуждены разбивать вещи на партии.
Anssssss
Большой! Это действительно должно быть отмечено как действительно правильный ответ!
SQL Police
4

Если вы используете SQL Server, вы можете настроить глобальные переменные для целых сценариев, например:

:setvar sourceDB "lalalallalal"

и использовать позже в скрипте как:

$(sourceDB)

Убедитесь, что режим SQLCMD включен в Server Managment Studi, вы можете сделать это через верхнее меню. Нажмите «Запрос» и включите режим SQLCMD.

Подробнее по теме можно найти здесь: Документация MS

DanteTheSmith
источник
1

Не уверен, что это поможет

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

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

execute sp_executesql @t
Шахкалпеш
источник
1

Временные таблицы сохраняются в операторах GO, поэтому ...

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

Это некрасиво, но работает

Ремко Нонхебель
источник
1

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

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

Тогда вы можете использовать это:

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)))

Вывод:

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

Вы также можете использовать их:

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

Выход exec dbo.MyVariableList:

Name    Value
test    42

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

Здесь используется временная таблица с ##префиксом, так что этого достаточно, чтобы выжить после оператора GO. Он предназначен для использования в одном скрипте.

И хранимые процедуры:

-- 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
Контанго
источник
0

Если вам просто нужно двоичное да / нет (например, если столбец существует), вы можете использовать его SET NOEXEC ONдля отключения выполнения операторов. SET NOEXEC ONработает по GO (по партиям). Но не забудьте снова включить EXEC с помощью SET NOEXEC OFFв конце скрипта.

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

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

Изорг
источник
0

Вы можете использовать NOEXEC, выполнив следующие шаги:

Создать таблицу

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

вставить версии процедуры и указатель на версию во временную таблицу #temp_procedure_version

--example указатель на версию_процедуры

вставить в temp_procedure_versionзначения (1.0, 'первая версия')

вставить в temp_procedure_versionзначения (2.0, 'final version')

затем получить версию процедуры, вы можете использовать условие where, как в следующем заявлении

Выберите @ProcedureVersion=ProcedureVersionиз #temp_procedure_versionгде pointer='first version'

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

- вставить сюда процедуру версии 1.0

Создайте процедуру версии 1.0 как .....

SET NOEXEC OFF -- execution is ON

Выберите @ProcedureVersion=ProcedureVersionиз #temp_procedure_versionгде указателя = «окончательного варианта»

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

Создайте процедуру версии 2.0 как .....

SET NOEXEC OFF -- execution is ON

- сбросить временную таблицу

Удалить таблицу #temp_procedure_version

Вив Патания
источник