Агрегировать побитовое ИЛИ в подзапросе - PullRequest
24 голосов
/ 20 октября 2010

С учетом следующей таблицы:

CREATE TABLE BitValues ( n int )

Возможно ли вычислить побитовое ИЛИ для n для всех строк в подзапросе ? Например, если BitValues ​​содержит эти 4 строки:

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

Я ожидаю, что подзапрос возвратит 7. Есть ли способ сделать это встроенным, без создания UDF ?

Ответы [ 10 ]

12 голосов
/ 20 октября 2010
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
7 голосов
/ 04 февраля 2011

Простое решение, представляющее собой сочетание решений @ AlexKuznetsov и @ Andomar.Битовая маска генерируется рекурсивным выражением Common Table, но более простым способом, чем в решении @ Andomar.Затем биты суммируются так же, как в решении @ AlexKuznetsov.В этом примере я предполагаю, что требуется 16-битная маска, следовательно, предел 65536.Вы можете указать N-битную маску, изменив 65536 на 2 ^ 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
5 голосов
/ 07 марта 2016

Я вижу, что этот пост довольно старый и есть несколько полезных ответов, но это довольно сумасшедший прямой метод ...

Select  
    SUM(DISTINCT(n & 0x01)) +
    SUM(DISTINCT(n & 0x02)) +
    SUM(DISTINCT(n & 0x04))
    as OrN
From BitValues
3 голосов
/ 18 июля 2013

Подготовка:

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 голосов
/ 20 октября 2010

Вы можете использовать переменную и сделать «побитовое или» (|) для каждой строки:

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
        )

Суммирует биты, для которых установлен любой бит в исходной таблице.

1 голос
/ 04 марта 2016

Это альтернатива, без 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

Первый пример должен быть медленнеечем с.Однако, когда вы используете 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;

Это немного хуже из-за повторения в коде, немного более читаемый и похожий по стоимости исполнения.

1 голос
/ 27 июля 2012

Я попытался использовать функцию 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
0 голосов
/ 04 августа 2016

Для меня это лучшее решение.

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 голосов
/ 20 октября 2010

Лучшим выбором для удобочитаемого и многократно используемого решения было бы написание собственного CLR Aggregate для выполнения побитового или.Учебник по созданию этого типа операции можно найти здесь: http://msdn.microsoft.com/en-us/library/91e6taax(VS.80).aspx

0 голосов
/ 20 октября 2010

Вы ищете что-то подобное?

РЕДАКТИРОВАТЬ : Как отмечалось в других комментариях, этот ответ был основан на предположении, что таблица BitValues ​​будет содержать только степени 2. Iпопытался прочитать между строк вопроса и сделать вывод для использования встроенного подзапроса.

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