Có cách nào để duy trì một biến trong suốt quá trình không?


82

Có cách nào để duy trì một biến trong suốt quá trình không?

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

Xem câu hỏi SO này cho dòng 'USE @bob'.


Tại sao bạn cần đặt tên bảng với tên DB đủ điều kiện? Tôi đoán một câu hỏi tương tự đã được hỏi trước câu hỏi này.
shahkalpesh

Và không có cách nào để xác định đủ điều kiện tên bảng với tên cơ sở dữ liệu trong một biến như vậy. Với câu hỏi trước đây của anh ấy về việc sử dụng một biến với câu lệnh USE, tôi đoán anh ấy sẽ cần thực hiện mọi thứ trong SQL động, với tất cả nỗi đau kéo dài đến bảng.
Lasse V. Karlsen

Tập lệnh thực sự tích hợp 4 cơ sở dữ liệu khác nhau. Tôi đã nhận xét hướng dẫn để tìm và thay thế dbName1, dbName2, dbName3 và dbName4. Tôi chỉ nghĩ rằng khách hàng chỉ cần đặt bốn biến sẽ ít bị lỗi hơn.
NitroxDM

Tiêu đề câu hỏi là một câu hỏi thực sự quan trọng, nhưng mã ví dụ thì thật tệ. Như câu trả lời được chấp nhận hiển thị, bạn không cần 'go' trong ví dụ của mình. Kết quả là câu trả lời được chấp nhận không trả lời câu hỏi trong tiêu đề của bạn.
Greg Woods

Câu trả lời:


31

Các golệnh được sử dụng để tách mã vào lô riêng biệt. Nếu đó chính xác là những gì bạn muốn làm, thì bạn nên sử dụng nó, nhưng nó có nghĩa là các lô thực sự riêng biệt và bạn không thể chia sẻ các biến giữa chúng.

Trong trường hợp của bạn, giải pháp là đơn giản; bạn chỉ có thể xóa các gocâu lệnh, chúng không cần thiết trong mã đó.

Lưu ý bên cạnh: Bạn không thể sử dụng một biến trong một usecâu lệnh, nó phải là tên của cơ sở dữ liệu.


1
Một số Câu lệnh SQL phải là câu lệnh đầu tiên trong một khối (vùng giữa các câu lệnh GO). Ví dụ: TẠO THỦ TỤC hoặc TẠO CHỨC NĂNG đều phải xuất hiện trước bất kỳ câu lệnh nào khác - ở đầu tập lệnh hoặc ngay sau câu lệnh GO (lưu ý: cho phép khoảng trắng và chú thích trước các câu lệnh này). Khi chạy các tập lệnh trong đó các câu lệnh như vậy phải xảy ra sau logic khác, các câu lệnh GO là bắt buộc. Nhưng tôi phải đồng ý rằng trong hầu hết các trường hợp, các câu lệnh GO có thể bị xóa.
Zarepheth

@Zarepheth: Điểm tốt. Nó không cần thiết trong mã cụ thể này, nhưng sẽ hữu ích khi biết rằng chúng có thể cần thiết trong một số trường hợp.
Guffa

1
Tại sao lại ủng hộ? Nếu bạn không giải thích những gì bạn nghĩ là sai, nó không thể cải thiện câu trả lời.
Guffa

2
@jwize: Không, bạn không cần phải tách chúng ra mà có thể được thực hiện trong cùng một khối.
Guffa

1
@Ben: goLệnh được sử dụng để chia mã thành các lô riêng biệt. Nếu đó là những gì bạn muốn làm, thì bạn nên sử dụng nó, nhưng nó có nghĩa là các lô thực sự riêng biệt và bạn không thể chia sẻ các biến giữa chúng.
Guffa

127

Sử dụng bảng tạm thời:

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

13
câu trả lời tuyệt vời ... bạn thực sự TRẢ LỜI câu hỏi được HỎI hơn là đưa ra một công việc xung quanh.
Cos Callis

1
Đây là câu trả lời đúng. Giải pháp tốt. Một điều thú vị nữa là, nếu bạn sử dụng một số lượng lớn các biến, tất cả chúng đều nằm trong một bảng dễ dàng truy cập, không phải cuộn lên và xuống SP để tìm các khai báo của bạn.
ColinMac

15

Tôi thích này câu trả lời từ câu hỏi này biến với GO toàn cầu

Điều này có thêm lợi ích là có thể làm những gì bạn muốn làm ban đầu.

Lưu ý là bạn cần bật chế độ SQLCMD (trong Truy vấn-> SQLCMD) hoặc bật chế độ này theo mặc định cho tất cả các cửa sổ truy vấn (Công cụ-> Tùy chọn rồi đến Kết quả truy vấn-> Theo mặc định, mở truy vấn mới trong chế độ SQLCMD)

Sau đó, bạn có thể sử dụng loại mã sau (hoàn toàn được trích xuất từ ​​câu trả lời tương tự của 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

Tôi đang chuyển hướng kết quả truy vấn sang một tệp khác (: out filename) trong chế độ SQLCMD và để nhận được kết quả đầu ra vào tệp, bạn phải thực hiện GO, vì vậy cú pháp này: setvar là cần thiết để thay thế các biến bình thường trong trường hợp đó vì bạn ' lại buộc phải chia nhỏ mọi thứ thành nhiều đợt.
Anssssss

Tuyệt quá! Đây thực sự nên được đánh dấu là câu trả lời chính xác thực sự!
Cảnh sát SQL

3

Nếu bạn đang sử dụng SQL Server, bạn có thể thiết lập các biến toàn cục cho toàn bộ tập lệnh như:

:setvar sourceDB "lalalallalal"

và sử dụng sau này trong tập lệnh dưới dạng:

$(sourceDB)

Đảm bảo rằng chế độ SQLCMD được bật trong Server Managment Studi, bạn có thể thực hiện điều đó qua menu trên cùng Nhấp vào Truy vấn và bật Chế độ SQLCMD.

Có thể tìm thấy thêm về chủ đề tại đây: Tài liệu MS


1

Không chắc, nếu điều này có ích

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

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

execute sp_executesql @t

1

Các bảng tạm thời được giữ lại trên các câu lệnh GO, vì vậy ...

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

Nó không đẹp, nhưng nó hoạt động


1

Tạo các thủ tục được lưu trữ của riêng bạn để lưu / tải vào một bảng tạm thời.

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

Sau đó, bạn có thể sử dụng cái này:

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

Đầu ra:

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

Bạn cũng có thể sử dụng:

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

Đầu ra của exec dbo.MyVariableList:

Name    Value
test    42

Nó chỉ ra rằng có thể liệt kê tất cả các biến trong một bảng thực sự khá hữu ích. Vì vậy, ngay cả khi bạn không tải một biến sau đó, nó rất tốt cho mục đích gỡ lỗi để xem mọi thứ ở một nơi.

Điều này sử dụng một bảng tạm thời có ##tiền tố, vì vậy nó chỉ đủ để tồn tại một câu lệnh GO. Nó được thiết kế để sử dụng trong một tập lệnh duy nhất.

Và các thủ tục được lưu trữ:

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

Nếu bạn chỉ cần có / không nhị phân (như nếu tồn tại một cột) thì bạn có thể sử dụng SET NOEXEC ONđể vô hiệu hóa việc thực thi các câu lệnh. SET NOEXEC ONhoạt động trên GO (theo lô). Nhưng nhớ để biến EXEC trở lại với SET NOEXEC OFFở phần cuối của kịch bản.

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

Điều này biên dịch các câu lệnh nhưng không thực thi chúng. Vì vậy, bạn sẽ vẫn gặp "lỗi biên dịch" nếu bạn tham chiếu lược đồ không tồn tại. Vì vậy, nó hoạt động để "tắt" tập lệnh chạy lần thứ 2 (những gì tôi đang làm), nhưng không hoạt động để tắt các phần của tập lệnh trong lần chạy đầu tiên, vì bạn sẽ vẫn gặp lỗi biên dịch nếu tham chiếu đến các cột hoặc bảng không vẫn chưa tồn tại.


0

Bạn có thể sử dụng NOEXEC theo các bước sau:

Tạo bảng

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

chèn các phiên bản thủ tục và con trỏ tới phiên bản vào bảng tạm thời #temp_procedure_version

- con trỏ thủ tục ví dụ

chèn vào temp_procedure_version các giá trị (1.0, 'phiên bản đầu tiên')

chèn vào temp_procedure_version các giá trị (2.0, 'phiên bản cuối cùng')

sau đó truy xuất phiên bản thủ tục, bạn có thể sử dụng điều kiện where như trong câu lệnh sau

Chọn @ProcedureVersion=ProcedureVersiontừ #temp_procedure_versionđâu pointer='first version'

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

--chèn thủ tục phiên bản 1.0 tại đây

Tạo thủ tục phiên bản 1.0 như .....

SET NOEXEC OFF -- execution is ON

Chọn @ProcedureVersion=ProcedureVersiontừ #temp_procedure_versionnơi pointer = 'phiên bản cuối cùng'

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

Tạo thủ tục phiên bản 2.0 như .....

SET NOEXEC OFF -- execution is ON

--drop bàn tạm thời

Thả bàn #temp_procedure_version

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.