子查询中的按位或聚合

问题描述 投票:0回答:11

给出下表:

CREATE TABLE BitValues ( n int )

是否可以计算子查询中

所有行
n的按位或?例如,如果 BitValues 包含以下 4 行:

+---+ | n | +---+ | 1 | | 2 | | 4 | | 3 | +---+

我希望子查询返回 7。有没有一种方法可以内联执行此操作,

无需创建 UDF

sql-server sql-server-2005 t-sql aggregation
11个回答
16
投票
我看到这篇文章已经很老了,并且有一些有用的答案,但这是一个非常疯狂的直接方法......

Select SUM(DISTINCT(n & 0x01)) + SUM(DISTINCT(n & 0x02)) + SUM(DISTINCT(n & 0x04)) as OrN From BitValues
    

13
投票
WITH Bits AS ( SELECT 1 AS BitMask UNION ALL SELECT 2 UNION ALL SELECT 4 UNION ALL SELECT 8 UNION ALL SELECT 16 ) SELECT SUM(DISTINCT BitMask) FROM ( SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 ) AS t JOIN Bits ON t.n & Bits.BitMask > 0
    

12
投票
一个简单的解决方案,混合了@AlexKuznetsov 和@Andomar 的解决方案。

位掩码是由递归公共表表达式生成的,但方式比 @Andomar 的解决方案更简单。
然后将这些位相加,就像@AlexKuznetsov 的解决方案一样。
在此示例中,我假设需要 16 位掩码,因此限制为 65536。您可以通过将 65536 更改为 2^N 来指示 N 位掩码。

WITH Bits AS ( SELECT 1 BitMask UNION ALL SELECT 2 * BitMask FROM Bits WHERE BitMask < 65536 -- recursion ) SELECT SUM(DISTINCT BitMask) FROM (SELECT 1 n UNION ALL SELECT 2 n UNION ALL SELECT 4 n UNION ALL SELECT 3 n) t INNER JOIN Bits ON t.n & Bits.BitMask > 0
    

3
投票
您可以使用变量并对每一行执行“按位或”(

|

):

declare @t table (n int) insert @t select 1 union select 2 union select 4 declare @i int set @i = 0 select @i = @i | n from @t select @i

这将打印

7

。  请注意,官方不支持在 select 中分配变量。

以更严格的 SQL 方式,您可以创建一个表,其中每一位对应一行。 该表有 31 行,因为第 32 位是负整数。 此示例使用递归 CTE 来创建该表:

declare @t table (n int) insert @t select 1 union select 2 union select 3 ; with bits(nr, pow) as ( select 1 , 1 union all select nr + 1 , pow * 2 from bits where nr <= 30 ) select sum(b.pow) from bits b where exists ( select * from @t t where b.pow & t.n > 0 )

这对源表中任何位被设置的位进行求和。


3
投票
准备工作:

if object_id(N'tempdb..#t', N'U') is not null drop table #t; create table #t ( n int ); insert into #t values (1), (2), (4), (3);

解决方案:

select max(n & 8) + max(n & 4) + max(n & 2) + max(n & 1) from #t;
    

2
投票
我尝试使用 COALESCE 函数并且它有效,例如:

DECLARE @nOrTotal INT SELECT @nOrTotal = COALESCE(@nOrTotal, 0) | nValor FROM (SELECT 1 nValor UNION SELECT 2 UNION SELECT 2) t SELECT @nOrTotal >> Result: 3
    

1
投票
这是一个替代方案,没有WITH(万岁!!!):

select sum(distinct isnull(n & BitMask, 0)) as resultvalue from ( SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 4 UNION ALL SELECT 3 ) t INNER JOIN (SELECT 0 BitMask union all SELECT 1 union all SELECT 2 union all SELECT 4 union all SELECT 8 union all SELECT 16 union all SELECT 32 union all SELECT 64 union all SELECT 128 union all SELECT 256 union all SELECT 512 union all SELECT 1024 union all SELECT 2048 union all SELECT 4096 union all SELECT 8192 union all SELECT 16384 union all SELECT 32768 union all SELECT 65536) Bits -- = SELECT POWER(2, 16) ON n & BitMask = BitMask;

另请考虑分组示例:

-- Setup temp table to produce an example -- create table #BitValues ( id int identity(1,1) ,value int ,groupby varchar(10) ) insert into #BitValues SELECT 1 AS value, 'apples' UNION ALL SELECT 2, 'apples' UNION ALL SELECT 4, 'apples' UNION ALL SELECT 3, 'apples' -- Bit operation: -- select groupby, sum(distinct isnull(value & BitMask, 0)) as tempvalue from #BitValues INNER JOIN (SELECT 0 BitMask union all SELECT 1 union all SELECT 2 union all SELECT 4 union all SELECT 8 union all SELECT 16 union all SELECT 32 union all SELECT 64 union all SELECT 128 union all SELECT 256 union all SELECT 512 union all SELECT 1024 union all SELECT 2048 union all SELECT 4096 union all SELECT 8192 union all SELECT 16384 union all SELECT 32768 union all SELECT 65536) Bits -- = SELECT POWER(2, 16) ON value & BitMask = BitMask group by groupby

第一个示例比WITH 慢。然而,当您将 GroupBy 与其他一些数据一起使用时,查询在成本方面基本上是相同的。

另一种方法是

select groupby ,max(case when n & 1 = 1 then 1 else 0 end) + max(case when n & 2 = 2 then 2 else 0 end) + max(case when n & 4 = 4 then 4 else 0 end) + max(case when n & 8 = 8 then 8 else 0 end) + max(case when n & 16 = 16 then 16 else 0 end) + max(case when n & 32 = 32 then 32 else 0 end) + max(case when n & 64 = 64 then 64 else 0 end) + max(case when n & 128 = 128 then 128 else 0 end) + max(case when n & 256 = 256 then 256 else 0 end) + max(case when n & 512 = 512 then 512 else 0 end) + max(case when n & 1024 = 1024 then 1024 else 0 end) as NewDNC from #BitValues group by groupby;

由于代码重复,这有点糟糕,可读性更强并且执行成本相似。


0
投票
您在寻找这样的东西吗?

编辑:正如其他评论中所指出的,这个答案是基于 BitValues 表仅包含 2 的幂的假设。我尝试阅读问题的字里行间并推断内联子查询的用途。

declare @BitValues table ( n int ) declare @TestTable table ( id int identity, name char(10), BitMappedColumn int ) insert into @BitValues (n) select 1 union all select 2 union all select 4 insert into @TestTable (name, BitMappedColumn) select 'Joe', 5 union all select 'Bob', 8 select t.id, t.name, t.BitMappedColumn from @TestTable t inner join (select SUM(n) as BitMask from @BitValues) b on t.BitMappedColumn & b.BitMask <> 0
    

0
投票
对我来说这是最好的解决方案。

declare @res int set @res=0 SELECT @res=@res|t.n FROM ( SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 ) AS t
    

0
投票
对于整数,这种聚合是可能的:

declare @testbits table(accountId int, bits int) insert into @testbits (accountId, bits) values (1, 1) , (2, 1) , (1, 3) select src.accountId , MAX(src.bits & 0x00000001) | MAX(src.bits & 0x00000002) | MAX(src.bits & 0x00000004) | MAX(src.bits & 0x00000008) | MAX(src.bits & 0x00000010) | MAX(src.bits & 0x00000020) | MAX(src.bits & 0x00000040) | MAX(src.bits & 0x00000080) | MAX(src.bits & 0x00000100) | MAX(src.bits & 0x00000200) | MAX(src.bits & 0x00000400) | MAX(src.bits & 0x00000800) | MAX(src.bits & 0x00001000) | MAX(src.bits & 0x00002000) | MAX(src.bits & 0x00004000) | MAX(src.bits & 0x00008000) | MAX(src.bits & 0x00010000) | MAX(src.bits & 0x00020000) | MAX(src.bits & 0x00040000) | MAX(src.bits & 0x00080000) | MAX(src.bits & 0x00100000) | MAX(src.bits & 0x00200000) | MAX(src.bits & 0x00400000) | MAX(src.bits & 0x00800000) | MAX(src.bits & 0x01000000) | MAX(src.bits & 0x02000000) | MAX(src.bits & 0x04000000) | MAX(src.bits & 0x08000000) | MAX(src.bits & 0x10000000) | MAX(src.bits & 0x20000000) | MAX(src.bits & 0x40000000) | MAX(src.bits & 0x80000000) from @testbits AS src
作为输入,我们得到:

accountId bits 1 1 2 1 1 3
作为输出,我们得到:

accountId (No column name) 1 3 2 1
    

-1
投票
对于可读且可重用的解决方案,最好的选择是编写一个自定义 CLR 聚合来执行按位或。可以在此处找到创建此类操作的教程:

http://msdn.microsoft.com/en-us/library/91e6taax(VS.80).aspx

© www.soinside.com 2019 - 2024. All rights reserved.