简体   繁体   English

在T-SQL中解构C#标志

[英]Deconstruct c# flags within t-sql

I am being asked to build a table with a bunch of bit fields to toggle a series of options. 我被要求建立一个带有一堆位域的表来切换一系列选项。 This is a perfect place for ac# flag enum to just combine all of these bits into a single int "options" field in the table. 这是ac#标志枚举仅将所有这些位组合到表中单个int“选项”字段中的理想场所。

Problem is that there are things other than c# that will need to read and query off these flags in the sql queries. 问题在于,除了c#之外,还有其他东西需要读取和查询sql查询中的这些标志。 I could find nothing that seems to be able to deconstruct an int into a series of flag values. 我什么都找不到,似乎无法将int构造为一系列标志值。 What I'm looking for (I think) is something that converts the int back to binary and then picks the n'th value and reports it as a bool of a specific option. 我正在寻找的(我认为)是将int转换回二进制然后选择第n个值并将其报告为特定选项的布尔值的东西。 I could do this manually but I'm worried about the performance hit plus the "going around your @ss to get to your elbow" effect of just trying to avoid a litany of bit columns with an over complicated solution. 我可以手动执行此操作,但我担心性能下降以及试图通过过于复杂的解决方案避免产生一连串的位列而产生的“四处走动@ss到达肘部”的效果。

This is a perfect place for ac# flag enum to just combine all of these bits into a single int "options" field in the table. 这是ac#标志枚举仅将所有这些位组合到表中单个int“选项”字段中的理想场所。

No, it is not. 不它不是。 It is the perfect place to show you are not aware that 16 bit fields in SQL are optimized in storage. 完美的地方表明您不知道SQL中的16位字段在存储中已优化。 And it is the perfect place for you to create a maintenance nightmare and demonstrate you can use an antipattern. 它是您创建维护噩梦并演示可以使用反模式的理想场所。

I could find nothing that seems to be able to deconstruct an int into a series of flag values 我什么都找不到,似乎无法将int分解为一系列标志值

There is nothing for that. 没有什么。 It is an antipattern. 这是一种反模式。

Bit columns. 位列。 That is how SQL wants it. 这就是SQL想要的方式。

I Think what you mean is you have say 10 options on set in a Flag Enum in sql and you want to save this to the database as a int value 我认为您的意思是说您在sql中的Flag Enum中设置了10个选项,并且要将其作为int值保存到数据库中

eg 例如

[Flags]
public enum Options
{
0 = None
1 = OP1
2 = OP2
4 = OP3
...
}

If you need to check this with something other then C# you would just need to use a Bit Wise operation to see what is checked. 如果您需要使用C#以外的其他方式进行检查,则只需使用位明智操作即可查看所检查的内容。 This can be done in SQL using the & operator( check here ), I guess most languages will have some implantation of bit wise also. 这可以在SQL中使用&运算符( 请在此处检查 )来完成,我想大多数语言也会有一些明智的选择。

For fun, I'm going to answer your question directly. 为了好玩,我将直接回答您的问题。 Over large data, this will not perform well, but neither will many bit columns. 对于大数据,这将无法很好地执行,但许多位列也将无法执行。

I think the answer for your overall design to have correct database design, not have a bunch of bit columns, and still perform well is to create a 1 to many table off of your main table, where a record existence means that the option is enabled. 我认为您的总体设计的答案是具有正确的数据库设计,没有一堆位列,并且仍然表现良好,是从主表中创建一个1对多表,其中存在记录意味着已启用该选项。 This can then be indexed well and perform well. 这样就可以很好地建立索引并执行得很好。 You can also follow a similar construct of lookup table that I'm using below, combined with SUM() across the multiple rows in the child table to convert the multiple records back to your C# enum. 您还可以遵循下面使用的类似查找表结构,并在子表中的多个行上结合SUM()将多个记录转换回C#枚举。 Updating the table can also be accomplished with a well crafted MERGE statement. 还可以通过精心设计的MERGE语句来更新表。 The code for that solution is a little drawn out however and outside the scope of your original question. 但是,该解决方案的代码略微超出了您原始问题的范围。

CREATE TABLE dbo.Data
    (
        ID int IDENTITY(1,1) NOT NULL PRIMARY KEY,
        Name varchar(50) NOT NULL,
        Flags int NOT NULL DEFAULT(0)
    );

CREATE TABLE dbo.Flag
    (
        Value int NOT NULL PRIMARY KEY,
        Name varchar(50) NOT NULL
    );

INSERT INTO dbo.Flag
    SELECT 1, 'Option A'
    UNION ALL
    SELECT 2, 'Option B'
    UNION ALL
    SELECT 4, 'Option C';

INSERT INTO dbo.Data (Name, Flags)
    SELECT 'George', 0
    UNION ALL
    SELECT 'Bob', 1
    UNION ALL
    SELECT 'Bill', 3;

-- for C#
SELECT
    d.ID,
    d.Name,
    d.Flags
FROM
    dbo.Data d;

-- for other callers vertical
SELECT
    d.ID,
    d.Name,
    d.Flags,
    f.Name AS Flag,
    f.Value
FROM
    dbo.Data d
        LEFT JOIN dbo.Flag f ON
            d.Flags & f.Value = f.Value;

-- for other callers horizontal
SELECT
    p.Name,
    p.[Option A],
    p.[Option B],
    p.[Option C]
FROM
    (
        SELECT
            d.Name,
            f.Name AS Flag,
            f.Value
        FROM
            dbo.Data d
                LEFT JOIN dbo.Flag f ON
                    d.Flags & f.Value = f.Value
    ) d
PIVOT
(
    COUNT(d.Value)
    FOR d.Flag IN ([Option A], [Option B], [Option C])
) p;

SELECT
    d.ID,
    d.Name,
    d.Flags
FROM
    dbo.Data d
WHERE
    EXISTS
    (
        SELECT *
        FROM
            dbo.Flag f
        WHERE
            d.Flags & f.Value = f.Value AND
            f.Name IN ('Option A', 'Option B')
    );

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM