Czy istnieje sposób na utrwalenie zmiennej w trakcie przejścia?

84

Czy istnieje sposób na utrwalenie zmiennej w trakcie przejścia?

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

Zobacz to pytanie SO dla wiersza „USE @bob”.

NitroxDM
źródło
Dlaczego musisz kwalifikować nazwę tabeli z nazwą bazy danych? Wydaje mi się, że podobne pytanie zostało zadane przed tym.
shahkalpesh
I nie ma możliwości zakwalifikowania nazw tabel z nazwą bazy danych w takiej zmiennej. Z jego poprzednim pytaniem dotyczącym używania zmiennej z instrukcją USE, zgaduję, że będzie musiał zrobić wszystko w dynamicznym SQL, z całym bólem, który ciągnie się na stół.
Lasse V. Karlsen
Rzeczywisty skrypt integruje 4 różne bazy danych. Skomentowałem instrukcje, aby znaleźć i zamienić dbName1, dbName2, dbName3 i dbName4. Pomyślałem tylko, że ustawienie czterech zmiennych byłoby mniej podatne na błędy.
NitroxDM
Tytuł pytania to naprawdę ważne pytanie, ale przykładowy kod jest okropny. Jak pokazuje zaakceptowana odpowiedź, w swoim przykładzie nie trzeba było „iść”. W rezultacie zaakceptowana odpowiedź nie odpowiada na pytanie w twoim tytule.
Greg Woods

Odpowiedzi:

31

Plik goPolecenie służy do dzielenia kodu w oddzielnych partiach. Jeśli to jest dokładnie to, co chcesz zrobić, powinieneś tego użyć, ale oznacza to, że partie są w rzeczywistości oddzielne i nie możesz udostępniać zmiennych między nimi.

W twoim przypadku rozwiązanie jest proste; możesz po prostu usunąćgo instrukcje, nie są one potrzebne w tym kodzie.

Uwaga dodatkowa: nie możesz użyć zmiennej w useinstrukcji, musi to być nazwa bazy danych.

Guffa
źródło
1
Niektóre instrukcje SQL muszą być pierwszą instrukcją w bloku (obszar między instrukcjami GO). Na przykład: CREATE PROCEDURE i CREATE FUNCTION muszą występować przed innymi instrukcjami - albo na początku skryptu, albo bezpośrednio po instrukcji GO (uwaga: spacje i komentarze są dozwolone przed tymi instrukcjami). W przypadku uruchamiania skryptów, w których takie instrukcje muszą występować po innej logice, wymagane są instrukcje GO. Ale muszę się zgodzić, że w większości przypadków oświadczenia GO można usunąć.
Zarepheth
@Zarepheth: Słuszna uwaga. Nie jest to potrzebne w tym konkretnym kodzie, ale warto wiedzieć, że w niektórych przypadkach mogą być potrzebne.
Guffa
1
Dlaczego głos przeciw? Jeśli nie wyjaśnisz, co Twoim zdaniem jest niewłaściwe, nie poprawi to odpowiedzi.
Guffa,
2
@jwize: Nie, nie musisz ich rozdzielać, można to zrobić w tym samym bloku.
Guffa
1
@Ben: goPolecenie służy do dzielenia kodu na oddzielne partie. Jeśli to jest to, co chcesz zrobić, powinieneś tego użyć, ale oznacza to, że partie są w rzeczywistości oddzielne i nie możesz udostępniać zmiennych między nimi.
Guffa
130

Użyj tabeli tymczasowej:

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
źródło
13
świetna odpowiedź ... faktycznie ODPOWIEDZIAŁEŚ NA ZADANE pytanie, zamiast dawać pracę.
Cos Callis
1
To jest właściwa odpowiedź. Niezłe rozwiązanie. Dodatkowo fajne jest to, że jeśli używasz dużej liczby zmiennych, wszystkie są w jednej łatwo dostępnej tabeli, bez przewijania w górę iw dół SP w poszukiwaniu deklaracji.
ColinMac
15

Wolę odpowiedź z tego pytania Global Variables with GO

Co ma dodatkową zaletę, że możesz robić to, co pierwotnie chciałeś zrobić.

Zastrzeżenie polega na tym, że musisz włączyć tryb SQLCMD (w sekcji Zapytanie-> SQLCMD) lub włączyć go domyślnie dla wszystkich okien zapytań (Narzędzia-> Opcje, a następnie Wyniki zapytania-> Domyślnie, otwieraj nowe zapytania w trybie SQLCMD)

Następnie możesz użyć następującego typu kodu (całkowicie wyrwany z tej samej odpowiedzi przez Oscara E. Fraxedasa 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
źródło
Przekierowałem wyjście zapytania do innego pliku (: out nazwa_pliku) w trybie SQLCMD i aby uzyskać dane wyjściowe opróżnione do pliku, musisz wykonać GO, więc składnia setvar jest niezbędna do zastąpienia normalnych zmiennych w tej sytuacji, ponieważ ' są zmuszeni do dzielenia rzeczy na partie.
Anssssss
Świetny! To faktycznie powinno być oznaczone jako prawdziwa poprawna odpowiedź!
SQL Police
4

Jeśli używasz SQL Server, możesz ustawić zmienne globalne dla całych skryptów, takich jak:

:setvar sourceDB "lalalallalal"

i użyj później w skrypcie jako:

$(sourceDB)

Upewnij się, że tryb SQLCMD jest włączony w programie Server Managment Studi. Możesz to zrobić w górnym menu Kliknij Zapytanie i włącz tryb SQLCMD.

Więcej na ten temat można znaleźć tutaj: Dokumentacja MS

DanteTheSmith
źródło
1

Nie jestem pewien, czy to pomoże

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

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

execute sp_executesql @t
shahkalpesh
źródło
1

Tabele tymczasowe są zachowywane w instrukcjach GO, więc ...

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

Nie jest ładna, ale działa

Remco Nonhebel
źródło
1

Utwórz własne procedury składowane, które zapisują / ładują do tabeli tymczasowej.

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

Następnie możesz użyć tego:

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

Wynik:

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

Możesz również użyć tych:

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

Wyjście exec dbo.MyVariableList:

Name    Value
test    42

Okazuje się, że możliwość wyświetlenia wszystkich zmiennych w tabeli jest w rzeczywistości całkiem przydatna. Więc nawet jeśli później nie załadujesz zmiennej, świetnie nadaje się do debugowania, aby zobaczyć wszystko w jednym miejscu.

Używa tymczasowej tabeli z rozszerzeniem ## prefiksem, więc wystarczy przetrwać instrukcję GO. Jest przeznaczony do użycia w jednym skrypcie.

Oraz procedury składowane:

-- 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
Contango
źródło
0

Jeśli potrzebujesz tylko binarnego tak / nie (np. Jeśli kolumna istnieje), możesz użyć, SET NOEXEC ONaby wyłączyć wykonywanie instrukcji. SET NOEXEC ONdziała w GO (w partiach). Ale należy pamiętać, aby włączyć EXEC plecami ze SET NOEXEC OFFna końcu skryptu.

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

To kompiluje instrukcje, ale ich nie wykonuje. Więc nadal będziesz otrzymywać „błędy kompilacji”, jeśli odwołasz się do schematu, który nie istnieje. Więc działa, aby "wyłączyć" skrypt po drugim uruchomieniu (co robię), ale nie działa, aby wyłączyć części skryptu przy pierwszym uruchomieniu, ponieważ nadal będziesz otrzymywać błędy kompilacji, jeśli odwołujesz się do kolumn lub tabel, które nie jeszcze nie istnieje.

yzorg
źródło
0

Możesz skorzystać z NOEXEC, postępując zgodnie z poniższymi krokami:

Utwórz tabelę

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

wstaw wersje procedur i wskaźnik do wersji do tabeli tymczasowej #temp_procedure_version

- przykładowy wskaźnik wersji_procedury

włóż w temp_procedure_version wartości (1.0, „pierwsza wersja”)

włóż w temp_procedure_version wartości (2.0, „wersja ostateczna”)

następnie pobierz wersję procedury, możesz użyć warunku gdzie, jak w poniższej instrukcji

Wybierz @ProcedureVersion=ProcedureVersionz #temp_procedure_versionktórym pointer='first version'

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

--wstaw procedurę w wersji 1.0 tutaj

Utwórz wersję procedury 1.0 jako .....

SET NOEXEC OFF -- execution is ON

Wybierz @ProcedureVersion=ProcedureVersionz #temp_procedure_versiongdzie pointer = „ostatecznej wersji”

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

Utwórz wersję procedury 2.0 jako .....

SET NOEXEC OFF -- execution is ON

- upuść tabelę tymczasową

Drop table #temp_procedure_version

Viv Pathania
źródło