在子查询中按位OR进行聚合

时间:2010-10-20 18:25:07

标签: sql-server sql-server-2005 tsql aggregation

鉴于下表:

CREATE TABLE BitValues ( n int )

是否可以为子查询中的所有行计算n的按位或?例如,如果BitValues包含这4行:

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

我希望子查询返回7.有没有办法在线创建而不创建UDF

10 个答案:

答案 0 :(得分:12)

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

答案 1 :(得分:7)

一个简单的解决方案,它是@ 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

答案 2 :(得分:4)

我看到这篇文章很老了,有一些有用的答案,但这是一个非常疯狂的直接方法......

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

答案 3 :(得分: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;

答案 4 :(得分:2)

您可以使用变量并为每行执行“按位或”(|):

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
        )

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

答案 5 :(得分:1)

我尝试使用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

答案 6 :(得分:1)

这是另一种选择,没有WITH(欢呼!!!):

public void start() {
    multicastService.get().start();
    udpService.get().start();
    connectionDispatcher.get().addConnectionAcceptor(this, false, "CONNECT", "\n\n");
    backgroundExecutor.scheduleWithFixedDelay(incomingValidator,
            timeBetweenValidates, timeBetweenValidates,
            TimeUnit.MILLISECONDS);
    _started = true;
}

还要考虑一个Group By示例:

    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;

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

另一种方法是

 -- 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

由于代码重复,执行成本更具可读性和相似性,因此情况更糟。

答案 7 :(得分: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

答案 8 :(得分: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

答案 9 :(得分:-1)

对于可读且可重复使用的解决方案,最好的办法是编写一个自定义CLR聚合来执行按位或。可以在此处找到创建此类操作的教程:http://msdn.microsoft.com/en-us/library/91e6taax(VS.80).aspx