循环遍历位数组以检索该位数组的子集

时间:2018-10-10 21:40:07

标签: sql sql-server tsql

更新

该问题已移至https://codereview.stackexchange.com/questions/205366/loop-through-bitarray-to-retrieve-subsets-of-that-bitarray。我没有删除它,因为它已经有了答案。

目标:

我想遍历位数组并将该位数组的子集存储在表中。

上下文:

我有一个包含48个元素的位数组,其中每个元素代表一个小时。我想从第二天开始24小时回顾一下,并检索最后一位为1的间隔。

我能够实现这一目标,但想知道是否有人可以提供更好的解决方案:)

我有一个名为[Numbers]的表,该表具有根据此链接https://www.mssqltips.com/sqlservertip/4176/the-sql-server-numbers-table-explained--part-1/创建的5000行。

脚本:

DECLARE @Ba NVARCHAR(48) = '000011110000000001110000000011111111000011110000'
DECLARE @numberOfIntervals INT = 24;
DECLARE @intervals TABLE( 
    SequenceId INT,
    [Periods] NVARCHAR(24)
)

INSERT INTO @intervals
SELECT number-1 AS [SequenceId], SUBSTRING(@Ba, number, @numberOfIntervals) AS [Values] 
FROM [dbo].[Numbers] 
WHERE  number > 1 AND number <= (LEN(@Ba)-(@numberOfIntervals-1)) AND RIGHT(SUBSTRING(@Ba, number, @numberOfIntervals), 1) = '1'

SELECT * FROM @intervals

结果:

[SequenceId]    [Values]
_________________________
5               111000000000111000000001
6               110000000001110000000011
7               100000000011100000000111
8               000000000111000000001111
9               000000001110000000011111
10              000000011100000000111111
11              000000111000000001111111
12              000001110000000011111111
17              111000000001111111100001
18              110000000011111111000011
19              100000000111111110000111
20              000000001111111100001111

1 个答案:

答案 0 :(得分:1)

BigInt值可以容纳64位。使用整数除法和按位运算符,您可以摆弄位。以下代码演示了从值中选择最低有效位,右移和重复。将where LSB = 1添加到最后的select将会滤除偶数。

declare @Foo as BigInt = 0x0F00700FF0F0; -- '0000 1111 0000 0000 0111 0000 0000 1111 1111 0000 1111 0000'
select @Foo as Foo, Cast( @Foo as VarBinary(6) ) as FooHex;

with ShiftedBits as (
  -- Start with the original value and pick off the least-significant-bit.
  select @Foo as Foo, 0 as Position, @Foo & 1 as LSB
  union all
  -- Right shift the value and repeat.
  select Foo / 2, Position + 1, Foo / 2 & 1
    from ShiftedBits
    where Position < 47 )
  -- Display the value, position and bit.
  select Foo, Cast( Foo as VarBinary(6) ) as FooHex, Position, LSB
    from ShiftedBits
    order by Position;

或者,如果您具有2的幂数表,则可以简单地掩盖位,而无需递归:

declare @Foo as BigInt = 0x0F00700FF0F0; -- '0000 1111 0000 0000 0111 0000 0000 1111 1111 0000 1111 0000'
select @Foo as Foo, Cast( @Foo as VarBinary(6) ) as FooHex;

-- Create a powers-of-two table.
declare @PowersOfTwo as Table ( P Int, P2 BigInt );
with PowersOfTwo as (
  select 0 as P, Cast( 1 as BigInt ) as P2
  union all
  select P + 1, P2 * 2
    from PowersOfTwo
    where P < 47 )
  insert into @PowersOfTwo
    select P, P2
      from PowersOfTwo;
select *, Cast( P2 as VarBinary(6) ) as P2Hex from @PowersOfTwo order by P;

-- Pick the bits.
select P, P2, Cast( P2 as VarBinary(6) ) as P2Hex,
  Cast( @Foo & P2 as VarBinary(6) ) as MaskedBit,
  Cast( @Foo / P2 as VarBinary(6) ) as ShiftedValue,
  case when @Foo & P2 = 0 then 'bad' else 'good' end as Keeper
  from @PowersOfTwo;