如何将逻辑 OR 应用于 BINARY 列,以便结果是所有正确的值?

How can I apply a logical OR to a BINARY column so the result is all the correct values?

我有一组结果存储在 binary(12) 列中。我正在寻找在不同时间针对特定条件设置的所有标志。它是这样的

Status Flags
1234 0x000000000000000000002000
5678 0x000000000000000000000000
1234 0x000000000000000000000040

我想做的是编写一个查询,例如

SELECT Status, OR(Flags)
FROM StatusTable
GROUP BY Status

给出结果

Status OR(Flags)
1234 0x000000000000000000002040
5678 0x000000000000000000000000

我可以找到让我手动对两个值进行“或”操作的示例,但没有找到对结果列应用“或”操作的示例。我已经大大简化了示例,但我们正在讨论具有数千种状态的数千个值(主要是 0x000000000000000000000000),这使得手动对它们进行 OR 操作变得不切实际。我想可以使用一个函数和一个游标来循环每个函数,但肯定有开箱即用的解决方案吗?

在TSQL中写这个会很难,如果你不能修复底层设计,你可以写一个.Net CLR User-Defined Aggregate。

在 C# 中,您有二元或运算符:Bitwise and shift operators.
您可以按照本指南编写 CLR 函数:CLR User-Defined Aggregates

T-SQL 不擅长位运算。但它确实有 &| 您需要分解每个标志,将其聚合在 table 上,然后将其打包回

由于缺少二进制 STRING_AGG.

,此查询并没有变得更容易
SELECT
    [Status],
    CAST(SUM(CASE WHEN byte = 1 THEN val END) AS binary(4)) +
    CAST(SUM(CASE WHEN byte = 5 THEN val END) AS binary(4)) +
    CAST(SUM(CASE WHEN byte = 9 THEN val END) AS binary(4)) AS OrFlags
FROM (
    SELECT [Status], v1.bytePos,
        MAX(CASE WHEN CAST(SUBSTRING(Flags, v1.bytePos, 4) AS int) & v2.bitt <> 0 THEN v2.bitt ELSE 0 END) AS val
    FROM @StatusTable
    CROSS JOIN (VALUES(1),(5),(9)) v1(bytePos)
    CROSS JOIN (VALUES
        (1),(2),(4),(8),(16),(32),(64),(128),(256),(512),(1024),(2048),(4096),(8192),(16384),(32768),(65536),(131072),(262144),(524288),(1048576),(2097152),(4194304),(8388608),(16777216),(33554432),(67108864),(134217728),(268435456),(536870912),(1073741824),(-2147483648)
    ) v2(bitt)
    GROUP BY [Status], v1.bytePos, v2.bitt
) t
GROUP BY Status

步骤如下:

  • 取基数table,交叉连接1,5,9每个32位整数的起始字节数。
  • 再次交叉连接所有位标志
  • Status 和 position/flag
  • 分组
  • Select出Status,整数位置,是否有任何值设置了这个标志
  • 最后有条件地总结标志并​​将结果转换回二进制

如果要实现 AND 聚合,请将 MAX 更改为 MIN