【问题标题】:PL/SQL Comparing bit strings, I need an AND operation?PL/SQL 比较位串,我需要 AND 操作吗?
【发布时间】:2011-09-13 09:39:40
【问题描述】:

我目前有一个脚本可以计算化学库指纹上的谷本系数。然而,在测试过程中,我发现由于我比较位串的方法(它只需要太长时间),我的实现无法切实扩大。见下文。这是我需要改进的循环。我已经对此进行了简化,因此它只是查看两个结构,真实脚本对结构数据集进行排列,但这会使我在这里遇到的问题过于复杂。

LOOP
-- Find the NA bit
SELECT SUBSTR(qsar_kb.fingerprint.fingerprint, var_fragment_id ,1) INTO var_na FROM qsar_kb.fingerprint where project_id = 1 AND structure_id = 1;

-- FIND the NB bit
SELECT SUBSTR(qsar_kb.fingerprint.fingerprint, var_fragment_id ,1) INTO var_nb FROM qsar_kb.fingerprint where project_id = 1 AND structure_id = 2;

-- Test for both bits the same
IF var_na > 0 AND var_nb > 0 then
var_tally := var_tally + 1;
END IF;

-- Test for bit in A on and B off
IF var_na > var_nb then
var_tna := var_tna + 1;
END IF

-- Test for bit in B on and A off.
IF var_nb > var_na then
var_tnb := var_tnb + 1;
END IF;

var_fragment_id := var_fragment_id + 1;
EXIT WHEN var_fragment_id > var_maxfragment_id;
END LOOP;

举个简单的例子

结构 A = '101010'

结构 B = '011001'

在我的真实数据集中,二进制的长度为 500 位及以上。 我需要知道:

1)Both 共有的 ON 位数

2)A中ON但B中关闭的位数

3)B中ON但B中关闭的位数

所以在这种情况下

1) = 1

2) = 2

3) = 2

理想情况下,我想改变我这样做的方式。我不想让每个字符串中的每个位都变得太费时,因为当我将整个系统扩展到数千个结构时,每个结构的指纹位字符串的长度顺序为 500-1000

我解决这个问题的逻辑是:

将两者的总位数设为 ON

A) = 3

B) = 3

然后执行 AND 操作,找出两者都有多少位 = 1

然后只需从总数中减去这个,就可以找到一个而不是另一个的位数。

那么如何对两个 0 和 1 的字符串执行 AND 类似操作以找到常见 1 的数量?

【问题讨论】:

  • Structure A = '101010'。这些字符串是由字符 0 和 1 还是由位组成的?
  • 它是一个由 0 和 1 组成的 varchar2。

标签: sql oracle plsql bit-manipulation logical-operators


【解决方案1】:

查看BITAND 函数。

BITAND 函数将其输入和输出视为位向量;输出是输入的按位与。

但是,根据文档,这仅适用于 2^128

【讨论】:

  • +1,尽管在这种情况下,我认为 OP 处理“1”和“0”的“字符串”而不是真正的二进制数据
  • 是的,他还提到实际上他需要长达 1000 位的长度,所以我的功能可能无法正常工作,除非他将字符串拆分为 128 位的块......
【解决方案2】:

您应该将 SELECT 移出循环。我很确定您花费 99% 的时间选择 1 位 500 次,您可以一次选择 500 位,然后循环遍历字符串:

DECLARE
   l_structure_a LONG;
   l_structure_b LONG;
   var_na        VARCHAR2(1);
   var_nb        VARCHAR2(1);
BEGIN
   SELECT MAX(decode(structure_id, 1, fingerprint)), 
          MAX(decode(structure_id, 2, fingerprint))
     INTO l_structure_a, l_structure_b
     FROM qsar_kb.fingerprint
    WHERE project_id = 1
      AND structure_id IN (1,2);
   LOOP
      var_na := substr(l_structure_a, var_fragment_id, 1);
      var_nb := substr(l_structure_b, var_fragment_id, 1);

      -- Test for both bits the same
      IF var_na > 0 AND var_nb > 0 THEN
         var_tally := var_tally + 1;
      END IF;   
      -- Test for bit in A on and B off
      IF var_na > var_nb THEN
         var_tna := var_tna + 1;
      END IF;   
      -- Test for bit in B on and A off.
      IF var_nb > var_na THEN
         var_tnb := var_tnb + 1;
      END IF;

      var_fragment_id := var_fragment_id + 1;
      EXIT WHEN var_fragment_id > var_maxfragment_id;
   END LOOP;
END;

编辑: 您也可以在单个 SQL 语句中执行此操作:

SQL> WITH DATA AS (
  2     SELECT '101010' fingerprint,1 project_id, 1 structure_id FROM dual
  3     UNION ALL SELECT '011001', 1, 2 FROM dual),
  4  transpose AS (SELECT ROWNUM fragment_id FROM dual CONNECT BY LEVEL <= 1000)
  5  SELECT COUNT(CASE WHEN var_na = 1 AND var_nb = 1 THEN 1 END) nb_1,
  6         COUNT(CASE WHEN var_na > var_nb THEN 1 END) nb_2,
  7         COUNT(CASE WHEN var_na < var_nb THEN 1 END) nb_3
  8    FROM (SELECT to_number(substr(struct_a, fragment_id, 1)) var_na,
  9                 to_number(substr(struct_b, fragment_id, 1)) var_nb
 10             FROM (SELECT MAX(decode(structure_id, 1, fingerprint)) struct_a,
 11                           MAX(decode(structure_id, 2, fingerprint)) struct_b
 12                      FROM DATA
 13                     WHERE project_id = 1
 14                       AND structure_id IN (1, 2))
 15            CROSS JOIN transpose);

      NB_1       NB_2       NB_3
---------- ---------- ----------
         1          2          2

【讨论】:

    【解决方案3】:

    我将通过更多信息来扩展 Lukas 的答案。

    Tom Kyte(通过 Jonathan Lewis)在互联网上搜索 revealed code 以在碱基之间进行转换。有一个函数to_dec 将接受一个字符串并将其转换为一个数字。我已经复制了下面的代码:

    将基数转换为十进制:

    create or replace function to_dec( 
      p_str in varchar2, 
      p_from_base in number default 16) return number
    is
        l_num   number default 0;
        l_hex   varchar2(16) default '0123456789ABCDEF';
    begin
        for i in 1 .. length(p_str) loop
            l_num := l_num * p_from_base + instr(l_hex,upper(substr(p_str,i,1)))-1;
        end loop;
        return l_num;
    end to_dec;
    

    将十进制转换为基数:

    create or replace function to_base( p_dec in number, p_base in number ) 
    return varchar2
    is
        l_str   varchar2(255) default NULL;
        l_num   number  default p_dec;
        l_hex   varchar2(16) default '0123456789ABCDEF';
    begin
        if ( trunc(p_dec) <> p_dec OR p_dec < 0 ) then
            raise PROGRAM_ERROR;
        end if;
        loop
            l_str := substr( l_hex, mod(l_num,p_base)+1, 1 ) || l_str;
            l_num := trunc( l_num/p_base );
            exit when ( l_num = 0 );
        end loop;
        return l_str;
    end to_base;
    

    可以调用此函数将字符串位图转换为数字,然后可以与 bitand 一起使用。这方面的一个例子是:

    select to_dec('101010', 2) from dual
    

    Oracle 仅真正提供BITAND(和BIT_TO_NUM,这在此处并不真正相关`作为一种执行逻辑操作的方式,但此处所需的操作是(A AND B)、(A AND NOT B)和( NOT A AND B)。所以我们需要一个将 A 转换为 NOT A 的 var。一个简单的方法是使用 translate。

    所以....最后的结果是:

    select 
      length(translate(to_base(bitand(data_A, data_B),2),'10','1')) as nb_1,
      length(translate(to_base(bitand(data_A, data_NOT_B),2),'10','1'))  as nb_2,
      length(translate(to_base(bitand(data_NOT_A, data_B),2),'10','1'))  as nb_3
    from (
      select 
        to_dec(data_A,2) as data_A, 
        to_dec(data_b,2) as data_B,
        to_dec(translate(data_A, '01', '10'),2) as data_NOT_A, 
        to_dec(translate(data_B, '01', '10'),2) as data_NOT_B
      from (
        select '101010' as data_A, '011001' as data_B from dual
      )
    )
    

    这比我开始写这个答案时所希望的要复杂一些,但它似乎确实有效。

    【讨论】:

      【解决方案4】:

      可以很简单地用这样的东西来完成:

      SELECT utl_raw.BIT_AND( t.A, t.B )                                                 SET_IN_A_AND_B,
             length(replace(utl_raw.BIT_AND( t.A, t.B ), '0', ''))                       SET_IN_A_AND_B_COUNT,
             utl_raw.BIT_AND( t.A, utl_raw.bit_complement(t.B) )                         ONLY_SET_IN_A,
             length(replace(utl_raw.BIT_AND( t.A, utl_raw.bit_complement(t.B) ),'0','')) ONLY_SET_IN_A_COUNT,
             utl_raw.BIT_AND( t.B, utl_raw.bit_complement(t.A) )                         ONLY_SET_IN_A,
             length(replace(utl_raw.BIT_AND( t.B, utl_raw.bit_complement(t.A) ),'0','')) ONLY_SET_IN_A_COUNT       
        FROM (SELECT '1111100000111110101010' A, '1101011010101010100101' B FROM dual) t
      

      确保您的位串具有偶数长度(如果它具有奇数长度,只需用零填充它)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-09
        • 1970-01-01
        • 2011-11-23
        • 2011-09-12
        • 2020-06-06
        • 1970-01-01
        相关资源
        最近更新 更多