计算一致性的位标志交集

时间:2013-02-19 14:57:46

标签: sql-server bit-fields

技术情况:

给定 SQL Server 2008 R2 中的int列,用于保存十进制“编码”位标志(范围从2 0 到2 30 ,因此有31个可用标志,最大值为1073741824)。该列可以包括整个可分配整数组合范围,所有这些都是对单个对象进行分类。

要解决的问题:

从有限数量的十进制“位整数”中,我们必须找到一个代表所有这些标志的某种交集的数字 - 就像你有3,10,524290时一样,很明显应该在结果中设置2,而其他位(1,8,524288)是有争议的。这是一个非常基本的例子。这可能是一组真实的输入数据(仅限前两列):

Occurences | Decimal bit field | Binary representation
     7     |   268435460       | 10000000000000000000000000100
     5     |   268435488       | 10000000000000000000000100000
     5     |         128       | 00000000000000000000010000000
     4     |          32       | 00000000000000000000000100000
     3     |           4       | 00000000000000000000000000100
     3     |   268435492       | 10000000000000000000000100100
     2     |          36       | 00000000000000000000000100100
     2     |         132       | 00000000000000000000010000100
     1     |         160       | 00000000000000000000010100000
   Occurences of particular bit: 3--------------------3-6--6--
     Desired output possibility: 10000000000000000000000100100

到目前为止的解决方案:

...在Transact-SQL中实现:

  1. 收集要评估的所有整数,并将它们连接成逗号分隔的字符串。
  2. 减少字符串,取出第一个数字,循环遍历:
  3. 检查被测者是否符合最大值(&-AND)并将最大值除以2(>=1时)。
  4. ......到目前为止? ó.Ò

    现在我为比特集输出了二进制代表。我考虑将这些位保存到31列临时表中以进行评估。但后来我开始思考:难道没有更聪明的方法吗? SQL Server速度超快,即使在反汇编10000个生成的整数时也是如此。但也许有一个内置函数来计算两个二进制位标志之间的“距离”。

    我承认这是一个复杂的问题,但我真的看到隧道尽头的灯光,即使它需要以间接的方式完成。它会变得更复杂,因为加权也应该在以后应用,因此2׬00010000 @ 100%显着性> 00010000 @ 40 %重要性。但是,当我生成一个摘要并且可用;-)

    时,我会尝试处理这个问题

1 个答案:

答案 0 :(得分:1)

您可以使用按位和操作

declare @i int
declare @x int
declare @cnt int
select @i=2147483647  -- 1111111 11111111 11111111 11111111
declare @t table (a int)
insert into @t values( 3),(10),(524290);

Select @i= (@i & a) from @t 


-- just for fun an output
set @x=1
set @cnt=0
While @cnt<31
  begin
  Print Case when @x & @i <> 0 then 'X' else ' ' end  +' ' + Cast(@x as Varchar(12))   
  Set @cnt=@cnt + 1
  if @cnt<31  Set @x=@x*2
  end 

或输出更好

declare @i int
declare @x int
declare @cnt int
select @i=2147483647  -- 1111111 11111111 11111111 11111111
Declare  @ref table(ref int) 
set @x=1
set @cnt=0
While @cnt<31
  begin
  insert into @ref Values(@x)
  Set @cnt=@cnt + 1
  if @cnt<31  Set @x=@x*2
  end 


declare @t table (a int)
insert into @t values( 3),(10),(524290);

Select @i= (@i & a) from @t 

Select * from @ref where ref&@i<>0 

作为对你的评论的回答

declare @i int
declare @x int
declare @cnt int
select @i=2147483647  -- 1111111 11111111 11111111 11111111
Declare  @ref table(ref int) 
set @x=1
set @cnt=0
While @cnt<31
  begin
  insert into @ref Values(@x)
  Set @cnt=@cnt + 1
  if @cnt<31  Set @x=@x*2
  end 


declare @t table (a int)
insert into @t values( 3),(5),(9),(17),(33),(65),(128);

Select @i= (@i & a) from @t 

Select a,Count(*) as BitCount
from @ref r
join @t t on t.a & r.ref<>0
group by a

Select ref,Count(*) as OCC from @ref r
join @t t on t.a & r.ref<>0
group by ref

Select ref,(Select count(*) from @t where a & r.ref<>0) as OCC
from @ref r