Tạo hằng số mức cơ sở dữ liệu (liệt kê) mà không sử dụng CLR?


9

Tôi có một số đối tượng SQL cần thực hiện các hành động thay thế dựa trên trạng thái mong muốn của yêu cầu. Có cách nào để tạo các hằng số mức cơ sở dữ liệu (liệt kê) có thể được chuyển đến các thủ tục được lưu trữ, các hàm có giá trị bảng và được sử dụng trong các truy vấn (không sử dụng CLR) không?

CREATE PROCEDURE dbo.DoSomeWork(@param1 INTEGER, ..., @EnumValue myEnumType)  AS ...;

và sau đó sử dụng nó:

EXEC doSomeWork 85, ..., (myEnumType.EnumValue1 + myEnumType.EnumValue2);

Trường hợp myEnumTypesẽ giữ một vài giá trị liệt kê.

Trong quy trình tôi sẽ có thể sử dụng @EnumValuevà kiểm tra nó dựa trên các giá trị myEnumTypeđể thực hiện công việc được yêu cầu. Tôi sẽ làm cho các giá trị của myEnumTypemột bitmask cho trường hợp tôi đang xem xét.

Đối với một ví dụ đơn giản, hãy xem xét một quy trình đắt tiền lấy một tập dữ liệu khổng lồ và giảm nó thành một tập dữ liệu nhỏ hơn nhưng vẫn rất lớn. Trong quá trình này, bạn cần thực hiện một số điều chỉnh ở giữa quá trình đó sẽ ảnh hưởng đến kết quả. Giả sử đây là bộ lọc cho (hoặc chống lại) một số loại bản ghi dựa trên một số trạng thái của phép tính trung gian trong phần giảm. Các @EnumValueloại myEnumTypecó thể được sử dụng để thử nghiệm cho việc này

SELECT   ...
FROM     ...
WHERE       (@EnumValue & myEnumType.EnumValue1 = myEnumType.EnumValue1 AND ...)
        OR  (@EnumValue & myEnumType.EnumValue2 = myEnumType.EnumValue2 AND ...)
        OR  ...

Các loại hằng số mức cơ sở dữ liệu này có thể có trong SQL Server mà không cần sử dụng CLR không?

Tôi đang tìm kiếm một bảng liệt kê mức cơ sở dữ liệu hoặc tập các hằng số có thể được truyền dưới dạng tham số cho các thủ tục, hàm được lưu trữ, v.v.

Câu trả lời:


9

Bạn có thể tạo một kiểu liệt kê trong SQL Server bằng Lược đồ XML.

Ví dụ Màu sắc.

create xml schema collection ColorsEnum as '
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Color">
        <xs:simpleType>
            <xs:restriction base="xs:string"> 
                <xs:enumeration value="Red"/>
                <xs:enumeration value="Green"/>
                <xs:enumeration value="Blue"/>
                <xs:enumeration value="Yellow"/>
            </xs:restriction> 
        </xs:simpleType>
    </xs:element>
</xs:schema>';

Điều đó cho phép bạn sử dụng một biến hoặc tham số của loại xml(dbo.ColorsEnum).

declare @Colors xml(dbo.ColorsEnum);
set @Colors = '<Color>Red</Color><Color>Green</Color>'

Nếu bạn cố gắng thêm một cái gì đó không phải là một màu

set @Colors = '<Color>Red</Color><Color>Ferrari</Color>';

bạn gặp lỗi

Msg 6926, Level 16, State 1, Line 43
XML Validation: Invalid simple type value: 'Ferrari'. Location: /*:Color[2]

Việc xây dựng XML như thế có thể hơi tẻ nhạt, ví dụ bạn có thể tạo một chế độ xem trợ giúp cũng giữ các giá trị được phép.

create view dbo.ColorsConst as
select cast('<Color>Red</Color>' as varchar(100)) as Red,
       cast('<Color>Green</Color>' as varchar(100)) as Green,
       cast('<Color>Blue</Color>' as varchar(100)) as Blue,
       cast('<Color>Yellow</Color>' as varchar(100)) as Yellow;

Và sử dụng nó như thế này để tạo ra sự liệt kê.

set @Colors = (select Red+Blue+Green from dbo.ColorsConst);

Nếu bạn muốn tạo chế độ xem một cách linh hoạt từ Lược đồ XML, bạn có thể trích xuất các màu bằng truy vấn này.

select C.Name
from (select xml_schema_namespace('dbo','ColorsEnum')) as T(X)
  cross apply T.X.nodes('//*:enumeration') as E(X)
  cross apply (select E.X.value('@value', 'varchar(100)')) as C(Name);

Việc liệt kê tất nhiên cũng có thể được sử dụng làm tham số cho các chức năng và thủ tục.

create function dbo.ColorsToString(@Colors xml(ColorsEnum))
returns varchar(100)
as
begin
  declare @T table(Color varchar(100));

  insert into @T(Color)
  select C.X.value('.', 'varchar(100)')
  from @Colors.nodes('Color') as C(X);

  return stuff((select ','+T.Color
                from @T as T
                for xml path('')), 1, 1, '');
end
create procedure dbo.GetColors
  @Colors xml(ColorsEnum)
as
select C.X.value('.', 'varchar(100)') as Color
from @Colors.nodes('Color') as C(X);
declare @Colors xml(ColorsEnum) = '
<Color>Red</Color>
<Color>Blue</Color>
';

select dbo.ColorsToString(@Colors);

set @Colors = (select Red+Blue+Green from dbo.ColorsConst);
exec dbo.GetColors @Colors;

6

Vì rõ ràng bạn đang sử dụng SQL Server 2016, tôi muốn loại bỏ tùy chọn ' có thể ' khác - SESSION_CONTEXT.

Bài viết của Leonard lobel, Sharing State trong SQL Server 2016 vớiSESSION_CONTEXT một số thông tin rất hay về chức năng mới này trong SQL Server 2016.

Tóm tắt một số điểm chính:

Nếu bạn đã từng muốn chia sẻ trạng thái phiên trên tất cả các quy trình và đợt được lưu trữ trong suốt vòng đời của kết nối cơ sở dữ liệu, bạn sẽ thích SESSION_CONTEXT. Khi bạn kết nối với SQL Server 2016, bạn sẽ nhận được một từ điển trạng thái hoặc những gì thường được gọi là túi trạng thái, một số nơi bạn có thể lưu trữ các giá trị, như chuỗi và số, sau đó truy xuất nó bằng một khóa mà bạn gán. Trong trường hợp SESSION_CONTEXT, khóa là bất kỳ chuỗi nào và giá trị là một sql_variant, có nghĩa là nó có thể chứa nhiều loại.

Khi bạn lưu trữ một cái gì đó SESSION_CONTEXT, nó sẽ ở đó cho đến khi kết nối đóng lại. Nó không được lưu trữ trong bất kỳ bảng nào trong cơ sở dữ liệu, nó chỉ tồn tại trong bộ nhớ miễn là kết nối vẫn còn tồn tại. Và bất kỳ và tất cả các mã T-SQL đang chạy bên trong các thủ tục, trình kích hoạt, hàm hoặc bất cứ thứ gì được lưu trữ có thể chia sẻ bất cứ điều gì bạn chuyển vào SESSION_CONTEXT.

Điều gần nhất như thế này chúng ta có từ trước đến nay CONTEXT_INFO, cho phép bạn lưu trữ và chia sẻ một giá trị nhị phân duy nhất dài tới 128 byte, kém linh hoạt hơn nhiều so với từ điển bạn có SESSION_CONTEXT, hỗ trợ nhiều giá trị của dữ liệu khác nhau các loại.

SESSION_CONTEXTrất dễ sử dụng, chỉ cần gọi sp_set_session_context để lưu giá trị theo khóa mong muốn. Khi bạn làm điều đó, bạn cung cấp khóa và giá trị của khóa học, nhưng bạn cũng có thể đặt tham số read_only thành true. Đây là khóa giá trị trong ngữ cảnh phiên, do đó, nó không thể thay đổi trong suốt thời gian còn lại của kết nối. Vì vậy, ví dụ, ứng dụng khách dễ dàng gọi thủ tục được lưu trữ này để đặt một số giá trị ngữ cảnh phiên ngay sau khi thiết lập kết nối cơ sở dữ liệu. Nếu ứng dụng đặt tham số read_only khi thực hiện điều này, thì các thủ tục được lưu trữ và mã T-SQL khác sau đó thực thi trên máy chủ chỉ có thể đọc giá trị, chúng không thể thay đổi những gì được cài đặt bởi ứng dụng đang chạy trên máy khách.

Để thử nghiệm, tôi đã tạo một trình kích hoạt đăng nhập máy chủ để đặt một số CONTEXT_SESSIONthông tin - một trong số SESSION_CONTEXTđó đã được đặt thành @read_only.

DROP TRIGGER IF EXISTS [InitializeSessionContext] ON ALL SERVER
GO
CREATE TRIGGER InitializeSessionContext ON ALL SERVER
FOR LOGON AS

BEGIN

    --Initialize context information that can be altered in the session
    EXEC sp_set_session_context @key = N'UsRegion'
        ,@value = N'Southeast'

    --Initialize context information that cannot be altered in the session
    EXEC sp_set_session_context @key = N'CannotChange'
        ,@value = N'CannotChangeThisValue'
        ,@read_only = 1

END;

Tôi đã đăng nhập như một người dùng hoàn toàn mới và có thể trích xuất SESSION_CONTEXTthông tin:

DECLARE @UsRegion varchar(20)
SET @UsRegion = CONVERT(varchar(20), SESSION_CONTEXT(N'UsRegion'))
SELECT DoThat = @UsRegion

DECLARE @CannotChange varchar(20)
SET @CannotChange = CONVERT(varchar(20), SESSION_CONTEXT(N'CannotChange'))
SELECT DoThat = @CannotChange

Tôi thậm chí đã cố gắng thay đổi thông tin ngữ cảnh 'read_only':

EXEC sp_set_session_context @key = N'CannotChange'
    ,@value = N'CannotChangeThisValue'

và đã nhận được một lỗi:

Msg 15664, Cấp 16, Trạng thái 1, Quy trình sp_set_session_context, Dòng 1 [Batch Start Line 8] Không thể đặt khóa 'Không thể thay đổi' trong ngữ cảnh phiên. Khóa đã được đặt là read_only cho phiên này.

Một lưu ý quan trọng về kích hoạt đăng nhập ( từ bài đăng này )!

Trình kích hoạt đăng nhập có thể ngăn chặn hiệu quả các kết nối thành công đến Cơ sở dữ liệu cho tất cả người dùng, bao gồm cả các thành viên của vai trò máy chủ cố định sysadmin. Khi trình kích hoạt đăng nhập ngăn kết nối, các thành viên của vai trò máy chủ cố định sysadmin có thể kết nối bằng cách sử dụng kết nối quản trị viên chuyên dụng hoặc bằng cách khởi động Cơ sở dữ liệu ở chế độ cấu hình tối thiểu (-f)


Một nhược điểm tiềm năng là điều này sẽ lấp đầy phiên bản bối cảnh phiên (không phải trên mỗi cơ sở dữ liệu). Tại thời điểm này, các tùy chọn duy nhất tôi có thể nghĩ đến là:

  1. Đặt Session_Contexttên cho các cặp tên-giá trị của bạn bằng cách thêm tiền tố vào tên cơ sở dữ liệu để không gây ra xung đột cho tên cùng loại trong cơ sở dữ liệu khác. Điều này không giải quyết được vấn đề xác định trước TẤT CẢ Session_Contextcác giá trị tên cho tất cả người dùng.
  2. Khi kích hoạt đăng nhập kích hoạt, bạn có quyền truy cập EventData(xml) mà bạn có thể sử dụng để trích xuất người dùng đăng nhập và dựa vào đó, bạn có thể tạo các Session_Contextcặp giá trị tên cụ thể .

4

Trong SQL Server, không (mặc dù tôi nhớ việc tạo các hằng số trong các gói của Oracle vào năm 1998 và đã bỏ lỡ việc có chúng trong SQL Server).

VÀ, tôi vừa thử nghiệm và thấy rằng bạn thậm chí không thể làm điều này với SQLCLR, ít nhất là không phải theo nghĩa là nó sẽ hoạt động trong mọi trường hợp. Việc giữ là các hạn chế đối với các tham số Thủ tục lưu trữ. Dường như bạn không thể có một .hoặc ::trong tên tham số. Tôi đã thử:

EXEC MyStoredProc @ParamName = SchemaName.UdtName::[StaticField];

-- and:

DECLARE @Var = SchemaName.UdtName = 'something';
EXEC MyStoredProc @ParamName = @Var.[InstanceProperty];

Trong cả hai trường hợp, nó thậm chí không vượt qua được giai đoạn phân tích cú pháp (được xác minh bằng cách sử dụng SET PARSEONLY ON;) do:

Msg 102, Cấp 15, Trạng thái 1, Dòng xxxxx
Cú pháp không chính xác gần '.'.

Mặt khác, cả hai phương pháp đều hoạt động với các tham số Hàm do người dùng xác định:

SELECT MyUDF(SchemaName.UdtName::[StaticField]);

-- and:

DECLARE @Var = SchemaName.UdtName = N'something';
SELECT MyUDF(@Var.[InstanceProperty]);

Vì vậy, cách tốt nhất bạn có thể làm là sử dụng SQLCLR để có thứ gì đó hoạt động trực tiếp với UDF, TVF, UDA (tôi giả sử) và truy vấn, sau đó gán cho các biến cục bộ khi cần sử dụng với Thủ tục lưu trữ:

DECLARE @VarInt = SchemaName.UdtName::[StaticField];
EXEC MyStoredProc @ParamName = @VarInt;

Đây là cách tiếp cận tôi đã thực hiện khi có cơ hội có giá trị enum thực tế (trái ngược với giá trị tra cứu nên có trong bảng tra cứu cụ thể về cách sử dụng / ý nghĩa của nó).


Đối với việc thử điều này với Hàm do người dùng xác định (UDF) để đưa ra giá trị "hằng số" / "enum", tôi không thể làm cho nó hoạt động theo cách chuyển nó thành tham số Thủ tục lưu trữ:

EXEC MyStoredProc @ParamName = FunctionName(N'something');

trả về lỗi "Cú pháp không chính xác", với SSMS làm nổi bật mọi thứ trong ngoặc đơn, ngay cả khi tôi thay thế chuỗi bằng một số hoặc dấu ngoặc đơn bên phải nếu không có tham số nào để truyền vào.

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.