使用t-sql计算二进制类型的真实位数

时间:2014-01-23 13:57:16

标签: sql tsql count binary varbinary

我需要找到二进制值中存在多少真位。

示例:

input: 0001101    output:3
input: 1111001    output:5

5 个答案:

答案 0 :(得分:4)

虽然两个答案都有效,但两者都有问题。循环不是最优的并且会破坏该值。两种解决方案都不能在select语句中使用。

可能更好的解决方案是按以下方式屏蔽

select @counter = 0 
+ case when @BinaryVariable2 & 1 = 1 then 1 else 0 end 
+ case when @BinaryVariable2 & 2 = 2 then 1 else 0 end 
+ case when @BinaryVariable2 & 4 = 4 then 1 else 0 end 
+ case when @BinaryVariable2 & 8 = 8 then 1 else 0 end 
+ case when @BinaryVariable2 & 16 = 16 then 1 else 0 end 
+ case when @BinaryVariable2 & 32 = 32 then 1 else 0 end 
+ case when @BinaryVariable2 & 64 = 64 then 1 else 0 end 
+ case when @BinaryVariable2 & 128 = 128 then 1 else 0 end 
+ case when @BinaryVariable2 & 256 = 256 then 1 else 0 end 
+ case when @BinaryVariable2 & 512 = 512 then 1 else 0 end 

这可以在select和update语句中使用。它也快一个数量级。 (在我的服务器上大约50次)

为了帮助您,您可能希望使用以下生成器代码

declare @x int = 1, @c int = 0
print ' @counter = 0 '  /*CHANGE field/parameter name */
while @c < 10  /* change to how many bits you want to see */
begin
print ' + case when @BinaryVariable2 & ' + cast(@x as varchar) + ' = ' + cast(@x as varchar) + ' then 1 else 0 end '   /* CHANGE the variable/field name */
select @x *=2, @c +=1
end 

另外需要注意的是:如果使用bigint或超过32位,则必须按照以下方式进行投射

print ' + case when @Missing & cast(' + cast(@x as varchar) + ' as bigint) = ' + cast(@x as varchar) + ' then 1 else 0 end '

享受

答案 1 :(得分:3)

DECLARE @BinaryVariable2 VARBINARY(10);

SET @BinaryVariable2 = 60; -- binary value is 111100

DECLARE @counter int  = 0

WHILE @BinaryVariable2 > 0 
  SELECT @counter +=@BinaryVariable2 % 2,  @BinaryVariable2 /= 2

SELECT @counter

结果:

4

答案 2 :(得分:0)

我已经留下了各种调试选择。

begin

    declare @bin as varbinary(20);
    declare @bitsSet as int;

    set @bitsSet = 0;
    set @bin = convert(varbinary(20), 876876876876);

    declare @i as int;

    set @i = 0

    select LEN(@bin), 'Len';

    while @i < LEN(@bin)
    begin
      declare @bit as varbinary(1);
      set @bit = SUBSTRING(@bin, @i, 1);
      select @bit, 'Bit';

      declare @power as int
      set @power = 0;

      while @power < 8
      begin
        declare @powerOf2 as int;
        set @powerOf2 = POWER(2,  @power);
        if @powerOf2 <> 0
            set @bitsSet = @bitsSet + (@bit & @powerOf2) / @powerOf2; -- edited to add the divisor
        select @power, @powerOf2;

        set @power = @power + 1;
      end;

      select @bitsSet;
      set @i = @i + 1;
    end;
    select @bitsSet, 'End'
end;

干杯 -

答案 3 :(得分:0)

您可以通过使用递归CTE将数据拆分为一个1字节值的表并计算该表的每个字节中所有正确的位来处理任意长度的二进制值...

DECLARE @data Varbinary(MAX) = Convert(Varbinary(MAX), N'We can count bits of very large varbinary values without a loop or number table if you like...');


WITH each ( byte, pos ) AS (
    SELECT Substring(@data, Len(@data), 1), Len(@data)-1 WHERE Len(@data) > 0
    UNION ALL
    SELECT Substring(@data, pos, 1), pos-1 FROM each WHERE pos > 0
    )
SELECT Count(*) AS [True Bits]
FROM each
CROSS JOIN (VALUES (1),(2),(4),(8), (16),(32),(64),(128)) [bit](flag)
WHERE each.byte & [bit].flag = [bit].flag
OPTION (MAXRECURSION 0);

答案 4 :(得分:0)

这是我的函数,它返回任何长度的VARBINARY中的1位数字。

SELECT dbo.f_nColumnsUpdated(0x0001101); -- Returns 3
SELECT dbo.f_nColumnsUpdated(0x1111001);  -- Returns 5
SELECT dbo.f_nColumnsUpdated(0x888888888888);  -- Returns 8
SELECT dbo.f_nColumnsUpdated(0x0001FEFF0000);  -- Returns 16
SELECT dbo.f_nColumnsUpdated(0x0001FEFF00000001FEFF0000); -- Returns 32

就我而言,我需要计算在Update触发器中修改的列数。我不想测试所有的UPDATE()与NOT UPDATE()列,因为列数可能会发生变化。

SET @NumRows INT = @@ROWCOUNT;  -- Top of Trigger

-- [snipped]

IF  @NumRows=1 --
    AND UPDATE(EndTimeActual) --
    AND UPDATE(EndTimeDate) --
    AND UPDATE(EndTimeStatus) --
    AND UPDATE(EndTimeValue) --
    AND dbo.f_nColumnsUpdated(COLUMNS_UPDATED())=4
    BEGIN
      -- Do Process Here
    END

CREATE FUNCTION dbo.f_nColumnsUpdated(
  @Bits VARBINARY(MAX))
RETURNS INT
AS
  BEGIN
      --
      -- Returns the # of Bits=1 in Arbitrary Length VARBINARY
      --
      DECLARE @Byte VARBINARY(1); -- 8 Bit Substring
      DECLARE @Index INT = 1; -- Loop Index
      DECLARE @nResult INT = 0; -- # of 1 Bits Discovered

      WHILE @Index <= DATALENGTH(@Bits)
        BEGIN
            SET @Byte = SUBSTRING(@Bits, @Index, 1);
            SET @nResult = @nResult
                     + CASE WHEN @Byte & 1 = 1 THEN 1 ELSE 0 END
                     + CASE WHEN @Byte & 2 = 2 THEN 1 ELSE 0 END
                     + CASE WHEN @Byte & 4 = 4 THEN 1 ELSE 0 END
                     + CASE WHEN @Byte & 8 = 8 THEN 1 ELSE 0 END
                     + CASE WHEN @Byte & 16 = 16 THEN 1 ELSE 0 END
                     + CASE WHEN @Byte & 32 = 32 THEN 1 ELSE 0 END
                     + CASE WHEN @Byte & 64 = 64 THEN 1 ELSE 0 END
                     + CASE WHEN @Byte & 128 = 128 THEN 1 ELSE 0 END;

            SET @Index = @Index + 1;
        END

      RETURN @nResult;
  END
GO