29

给定下表:

CREATE TABLE BitValues ( n int )

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

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

我希望子查询返回 7。有没有办法在不创建 UDF 的情况下内联?

4

10 回答 10

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
于 2010-10-20T19:02:32.997 回答
12

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

Select  
    SUM(DISTINCT(n & 0x01)) +
    SUM(DISTINCT(n & 0x02)) +
    SUM(DISTINCT(n & 0x04))
    as OrN
From BitValues
于 2016-03-07T20:21:41.090 回答
9

一个简单的解决方案,它混合了@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
于 2011-02-04T11:24:20.793 回答
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;
于 2013-07-18T14:06:29.210 回答
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。请注意,官方不支持在选择中分配变量。

以更严格的 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
        )

这将源表中设置了任何位的位相加。

于 2010-10-20T18:28:45.107 回答
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
于 2012-07-27T18:25:54.550 回答
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;

还要考虑一个 Group By 示例:

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

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

于 2016-03-04T17:12:13.290 回答
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
于 2010-10-20T18:33:33.927 回答
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
于 2016-08-04T12:46:29.580 回答
-1

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

于 2010-10-20T18:52:03.083 回答