Подтвердить что ты не робот

Совокупное побитовое ИЛИ в подзапросе

Учитывая следующую таблицу:

CREATE TABLE BitValues ( n int )

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

Простое решение, представляющее собой сочетание решений @AlexKuznetsov и @Andomar.
Бит-маска генерируется рекурсивным общим выражением таблицы, но более простым способом, чем в решении @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

Ответ 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

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

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

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

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

Вы ищете что-то вроде этого?

РЕДАКТИРОВАТЬ. Как отмечалось в других комментариях, этот ответ основывался на предположении, что таблица 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

Ответ 9

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

declare @res int
set @res=0    
SELECT  @[email protected]|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

Ответ 10

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