根据多行中的值创建字符串

时间:2012-12-05 17:12:10

标签: sql oracle

Oracle 11G

  • 表1具有T1_ID
  • 表2具有T1_ID和表示二进制数

    的字符串(BIN_STR)
    1. 如果Table1.T1_ID不在Table2中,则返回0是字符串
    2. 如果Table1.T1_ID在Table2中只找到一次,则返回值为Table2中的字符串
    3. 如果在表2中多次找到Table1.T1_ID,则返回值必须是表2中字符串的按位OR。

示例:

表1

T1_ID 
    1 
    2 
    3

表2有这些记录

T1_ID   BIN_STR 
    2    '0101'
    3    '0100'
    3    '1000'

结果集需要

T1_ID   BIN_STR 
    1    '0000'
    2    '0101'
    3    '1100'

我有:

SELECT Tbl1.T1_ID,
       CASE (SELECT COUNT(*) FROM Table2 Tbl2 WHERE Tbl1.T1_ID = Tbl2.T1_ID)
         WHEN 0 THEN '0000'
         WHEN 1 THEN (SELECT Tbl2.BIN_STR FROM Table2 Tbl2 WHERE Tbl1.T1_ID = Tbl2.T1_ID)
         ELSE ???
       END AS "BINSTR"
FROM Table1 Tbl1

我知道这可以通过存储过程的自定义函数调用来完成。在SQL PLUS中有没有办法做到这一点?

2 个答案:

答案 0 :(得分:0)

我认为你需要的是按位或聚合函数。并且,Oracle提供它(参见http://docs.oracle.com/cd/E17952_01/refman-5.1-en/group-by-functions.html#function_bit-or)。

您需要将值转换为BIGINT。这假设您在字符串中少于64位:

select t1_id, cast(bit_or(cast(bin_str as bigint)) as varchar(255))
from table2
group by t1_id

答案 1 :(得分:0)

在SQL 11g中有几种方法可以做到这一点。这是行动中的MODEL条款:

SQL> with data as (select t1.t1_id, nvl(t2.bin_str, '0000') bin_str
  2    from table1 t1
  3         left outer join table2 t2
  4                      on t1.t1_id = t2.t1_id)
  5  select t1_id, new_bin_str bin_str
  6    from (select *
  7    from data
  8   model
  9   partition by (t1_id)
 10   dimension by (row_number() over (partition by t1_id order by bin_str) rn )
 11   measures (bin_str, cast(null as varchar2(16)) new_bin_str,
 12   count(*) over (partition by t1_id ) c)
 13  rules (
 14  new_bin_str[any] order by rn  =
 15                    case when bin_str[cv() - 1 ] is null
 16               then bin_str[cv()]
 17                        else cast(utl_raw.bit_or(new_bin_str[cv()-1],bin_str[cv()]) as varchar2(16))
 18           end ))
 19   where c = rn;

     T1_ID BIN_STR
---------- --------------------
         1 0000
         2 0101
         3 1100
让我们打破这个。

SQL> with data as (select t1.t1_id, nvl(t2.bin_str, '0000') bin_str
  2    from table1 t1
  3         left outer join table2 t2
  4                      on t1.t1_id = t2.t1_id)

这只是我们的基本查询,其中包含我们感兴趣的所有数据。

现在我们需要XOR(通过utl_raw.bit_or提供),并且只显示每个T1_ID的最终结果。

model子句可以为我们做到这一点。首先我们定义一个“分区”,因为我们希望计算被隔离到特定的T1_ID。

  9   partition by (t1_id)

接下来,我们对每一行应用订单,这样我们每次都以相同的方式执行操作。即对于T1_ID = 3,你有0101和1100所以我们想做0101 XOR 1100并计算结果。如果有第三个二进制“0001”,我们想要0101 XOR 1100 = res然后res XOR 0001,依此类推。我们只对最终结果感兴趣,所以我们最终可以使用行号来为我们过滤:

 10   dimension by (row_number() over (partition by t1_id order by bin_str) rn )

我们也将维持每T1_ID行的计数:

 12   count(*) over (partition by t1_id ) c)

rn = c这是每个T1_ID数据集中的最终结果。

措施条款

 11   measures (bin_str, cast(null as varchar2(16)) new_bin_str,

现在将成为我们的目标。即bin_str,但是,您也看到我已经定义了一个空白列new_bin_str。这将是我们的保留列,它保存XOR操作的结果。

现在计算部分是执行XOR的部分:

14 new_bin_str [any]顺序由rn =  bin_str [cv() - 1]为null时的15种情况  16然后bin_str [cv()]  17其他强制转换(utl_raw.bit_or(new_bin_str [cv() - 1],bin_str [cv()])为varchar2(16))  18结束))

所以说的是,对于第一行的每一行(new_bin_str [ANY]),只使用bin_str的值,但对于每个后续行,我们采用先前的new_bin_str和XOR到当前bin_str值。这给我们以下输出:

     T1_ID         RN BIN_STR              NEW_BIN_STR               C
---------- ---------- -------------------- ---------------- ----------
         1          1 0000                 0000                      1
         2          1 0101                 0101                      1
         3          1 0100                 0100                      2
         3          2 1000                 1100                      2

你现在可以看到NEW_BIN_STR有我们需要的结果,但它也有所有的中间“工作”。所以我们过滤c = rn最终得到:

     T1_ID         RN BIN_STR              NEW_BIN_STR               C
---------- ---------- -------------------- ---------------- ----------
         1          1 0000                 0000                      1
         2          1 0101                 0101                      1
         3          2 1000                 1100                      2

在外部选择中,我只需选择T1_IDNEW_BIN_STR

其他方式可以是递归因子子查询,用户定义的聚合(例如像listagg但是做了XOR的聚合)或者甚至可能是xquery(没有想到一个但是它也可能有能力做到这一点)。