2012-11-30 26 views
16

tôi sử dụng Flags trên enums trong C# và mọi thứ đều tốt nhưng muốn sử dụng một cái gì đó tương tự trong SQL trong các tình huống sau:SQL Server Bitwise cư xử như Flags C# Enum

Chúng tôi muốn trả về một danh sách người dùng được một phần của một danh sách hoặc điều kiện như vậy:

ConditionOne = 2 
ConditionTwo = 4 
ConditionThree = 8 

vv ...

Chúng tôi sẽ có người sử dụng với một số trong những điều kiện đối với họ như vậy:

User1: 6 (conditions 1 and 2) 
User2: 4 (condition 2) 
User3: 14 (conditions 1, 2 and 3) 

etc ...

Chúng tôi muốn có thể thực hiện truy vấn nơi chúng tôi nói có tất cả người dùng với điều kiện 1 và trong trường hợp này sẽ trả lại người dùng 1 và 3 mặc dù họ cũng có các điều kiện khác.

Bất kỳ thông tin chi tiết nào sẽ được đánh giá cao, chỉ sử dụng Cờ trong C# và không trực tiếp trong Sql Server.

+4

Nó sẽ được nhiều hơn "SQL-like" để lưu trữ này thông tin trong một bảng nhiều người. Vì vậy, bạn sẽ lưu trữ các hàng '(1,1), (1,2), (2,2), (3,1), (3,2), (3,3)' trong một bảng riêng biệt. Nó sẽ làm cho truy vấn tự nhiên hơn, và cung cấp các cơ hội lập chỉ mục. –

Trả lời

11

Toán tử bitwise trong SQL là &. Mệnh đề WHERE cần phải đánh giá một biểu BOOLEAN, như thế này:

create table #temp (id int, username varchar(20), flags int) 

insert into #temp values 
(1, 'User1', 6), 
(2, 'User2', 4), 
(3, 'User3', 14) 

declare @ConditionOne int = 2 

select * 
from #temp 
where flags & @ConditionOne <> 0 

drop table #temp 

Truy vấn này trả về dữ liệu sau đây:

id   username    flags 
----------- -------------------- ----------- 
1   User1    6 
3   User3    14 
14

Trong khi các nhà điều hành Bitwise đề xuất bởi James sẽ làm việc, nó sẽ không thể rất biểu diễn trong cơ sở dữ liệu quan hệ, đặc biệt khi bạn cố gắng mở rộng tới hàng triệu bản ghi. Lý do là các hàm trong mệnh đề where không được sargable (chúng ngăn cản chỉ mục tìm kiếm).

Điều tôi sẽ làm là tạo một bảng chứa tất cả các kết hợp cờ và điều kiện có thể có, điều này sẽ cho phép chỉ mục tìm kiếm điều kiện.

Populate FlagConditions. Tôi đã sử dụng một đơn (tinyint). bạn nên cần thêm Flags, bạn sẽ có thể mở rộng tiếp cận này:

CREATE TABLE FlagConditions (
     Flag TINYINT 
    , Condition TINYINT 
    , CONSTRAINT Flag_Condition PRIMARY KEY CLUSTERED (Condition,Flag) 
); 

CREATE TABLE #Flags (
     Flag TINYINT IDENTITY(0,1) PRIMARY KEY CLUSTERED 
    , DummyColumn BIT NULL); 
GO 

INSERT #Flags 
     (DummyColumn) 
SELECT NULL; 
GO 256 

CREATE TABLE #Conditions(Condition TINYINT PRIMARY KEY CLUSTERED); 

INSERT #Conditions (Condition) 
    VALUES (1),(2),(4),(8),(16),(32),(64),(128); 

INSERT FlagConditions (Flag, Condition)   
    SELECT 
    Flag, Flag & Condition 
    FROM #Flags f 
    CROSS JOIN #Conditions c 
    WHERE Flag & Condition <> 0; 

DROP TABLE #Flags; 
DROP TABLE #Conditions; 

Bây giờ bạn có thể sử dụng bảng FlagConditions bất cứ lúc nào bạn cần phải tìm kiếm hiệu quả trên một điều kiện enum Bitwise:

DECLARE @UserFlags TABLE (Username varchar(10), Flag tinyint); 

INSERT @UserFlags(Username, Flag) 
    VALUES ('User1',6),('User2',4),('User3',14); 

DECLARE @Condition TINYINT = 2; 

SELECT u.* 
FROM @UserFlags u 
INNER JOIN FlagConditions fc ON u.Flag = fc.Flag 
WHERE fc.Condition = @Condition; 

Điều này trả về:

Username Flag 
---------- ---- 
User1  6 
User3  14 

DBA của bạn sẽ cảm ơn bạn đã thực hiện tuyến đường được định hướng này.

3

tôi đã gần như cùng một vấn đề và có thể đưa ra giải pháp như:

SELECT t.value 
    , ISNULL(t.C1 + ', ', '') + ISNULL(t.C2, '') + ISNULL(', ' + t.C3, '') AS [type] 
FROM 
(
    SELECT value, 
     CASE WHEN (type & 2) <> 0 THEN 'Type1' END AS C1, 
     CASE WHEN (type & 4) <> 0 THEN 'Type2' END AS C2, 
     CASE WHEN (type & 8) <> 0 THEN 'Type3' END AS C3 
    FROM db.Agent 
) t 

và kết quả được như sau:

value  type 
---------- ------------------------------------ 
14   Type1, Type2, Type3 
12   Type2, Type3 
14   Type1, Type2, Type3 
Các vấn đề liên quan