嵌套的依赖sql问题

时间:2010-02-11 17:06:05

标签: sql oracle join

我正在尝试为依赖于安全性的报告解决方案实施前端。用户有12个级别的嵌套条件可供选择,每个级别的值都会影响下面的所有值。

因此页面上的标准选择(每个都是下拉列表)看起来像这样:

标准1
标准2
...
标准12

有一个安全表,其中包含每个用户可用的值,具有以下结构:

EmployeeID | Criteria_1_valid_Value | C2_valid_Value | ... | C12_valid_Value
x0001 | c1 | c2 | ... | c12

并且每个Employee在此表中将有一行或多行。可以把它想象成一个扁平化的树,将Criteria1作为根节点。

基于键,更改标准1将影响标准2到12中可见的值。同样,更改标准2中的值会影响标准3到标准12中可用的值。在每个级别,是一个选项,用于选择“所有值”,在内部用空格表示,用于查找。所以我需要在查找表/视图中有一个表示,它考虑到一个或多个级别可能有空格。

我正在努力寻找一种方法,使用sql为每个Criteria字段构建查找视图/表,而不必诉诸于硬编码。

例如,要构建条件2的查找,sql可能如下所示:

select EmployeeID, Criteria1, Criteria2
from Security
Union
select EmployeeID, ' ', Criteria2
from Security
Union
select EmployeeID, Criteria1, ' '
from Security
UNION
select EmployeeID, ' ', ' '
from Security

等等。不幸的是,当然,有12个级别,最后一个工作到2 ^ 12个工会,这很坦率地闻起来。

我已经尝试在批处理中为每个级别构建一个表,在每个级别之后提交,然后使用前面的表连接到Security表来构建下一个每个单独的UNION,但我似乎无法得到加入以适应空间。

我不知道我是否过度思考或完全遗漏了某些东西,但我觉得必须有一个更简单的解决方案。

编辑:这是在Oracle上,我正在使用ERP产品作为底层技术。

EDIT2:感谢大家的投入。我在下面的@Alex Poole的示例proc中使用连接正确地连接了正确的连接:

and (v_Criteria_1 = ' ' or Criteria_1_valid_Value = v_Criteria_1)

我错过了v_Criteria_1 = ' ' or

所以我现在已经正确加载了表(足够)。这将转变为调整/优化练习。我将看看来自@Alex Poole的proc和@ JD_55的算术方法,我认为这可能非常快。

5 个答案:

答案 0 :(得分:1)

考虑一系列左外连接,每个标准项取决于先前标准的值。当左连接产生空结果时,您可以使用NVL()函数返回空格而不是空值:

select a.employeeId, 
   nvl(c1.criteria_1, ' '), 
   nvl(c2.criteria_2, ' '), 
   nvl(c3.criteria_3, ' '), 
   nvl(c4.criteria_4, ' '), 
   nvl(c5.criteria_5, ' '), 
   nvl(c6.criteria_6, ' '), 
   nvl(c7.criteria_7, ' '), 
   nvl(c8.criteria_8, ' '), 
   nvl(c9.criteria_9, ' '), 
   nvl(c10.criteria_10, ' '), 
   nvl(c11.criteria_11, ' '), 
   nvl(c12.criteria_12, ' ')
from security as a,
 left outer join security as c1
        on (c1.employeeId = a.employeeId)
 left outer join security as c2
        on (c2.employeeId = a.employeeId and 
            c2.criteria_1 = a.criteria_1)
 left outer join security as c3
        on (c3.employeeId = a.employeeId and 
            c3.criteria_1 = a.criteria_1 and 
            c3.criteria_2 = a.criteria_2)
 left outer join security as c4
        on (c4.employeeId = a.employeeId and 
            c4.criteria_1 = a.criteria_1 and 
            c4.criteria_2 = a.criteria_2 and
            c4.criteria_3 = a.criteria_3)
 left outer join security as c5
        on (c5.employeeId = a.employeeId and 
            c5.criteria_1 = c1.criteria_1 and 
            c5.criteria_2 = a.criteria_2 and
            c5.criteria_3 = a.criteria_3 and
            c5.criteria_4 = a.criteria_4)
 left outer join security as c6
        on (c6.employeeId = a.employeeId and 
            c6.criteria_1 = c1.criteria_1 and 
            c6.criteria_2 = a.criteria_2 and
            c6.criteria_3 = a.criteria_3 and
            c6.criteria_4 = a.criteria_4 and
            c6.criteria_5 = a.criteria_5)
 left outer join security as c7
        on (c7.employeeId = a.employeeId and 
            c7.criteria_1 = c1.criteria_1 and 
            c7.criteria_2 = a.criteria_2 and
            c7.criteria_3 = a.criteria_3 and
            c7.criteria_4 = a.criteria_4 and
            c7.criteria_5 = a.criteria_5 and
            c7.criteria_6 = a.criteria_6)
 left outer join security as c8
        on (c8.employeeId = a.employeeId and 
            c8.criteria_1 = c1.criteria_1 and 
            c8.criteria_2 = a.criteria_2 and
            c8.criteria_3 = a.criteria_3 and
            c8.criteria_4 = a.criteria_4 and
            c8.criteria_5 = a.criteria_5 and
            c8.criteria_6 = a.criteria_6 and
            c8.criteria_7 = a.criteria_7)
 left outer join security as c9
        on (c9.employeeId = a.employeeId and 
            c9.criteria_1 = c1.criteria_1 and 
            c9.criteria_2 = a.criteria_2 and
            c9.criteria_3 = a.criteria_3 and
            c9.criteria_4 = a.criteria_4 and
            c9.criteria_5 = a.criteria_5 and
            c9.criteria_6 = a.criteria_6 and
            c9.criteria_7 = a.criteria_7 and
            c9.criteria_8 = a.criteria_8)
 left outer join security as c10
        on (c10.employeeId = a.employeeId and 
            c10.criteria_1 = c1.criteria_1 and 
            c10.criteria_2 = a.criteria_2 and
            c10.criteria_3 = a.criteria_3 and
            c10.criteria_4 = a.criteria_4 and
            c10.criteria_5 = a.criteria_5 and
            c10.criteria_6 = a.criteria_6 and
            c10.criteria_7 = a.criteria_7 and
            c10.criteria_8 = a.criteria_8 and
            c10.criteria_9 = a.criteria_9)
 left outer join security as c11
        on (c11.employeeId = a.employeeId and 
            c11.criteria_1 = c1.criteria_1 and 
            c11.criteria_2 = a.criteria_2 and
            c11.criteria_3 = a.criteria_3 and
            c11.criteria_4 = a.criteria_4 and
            c11.criteria_5 = a.criteria_5 and
            c11.criteria_6 = a.criteria_6 and
            c11.criteria_7 = a.criteria_7 and
            c11.criteria_8 = a.criteria_8 and
            c11.criteria_9 = a.criteria_9 and
            c11.criteria_10 = a.criteria_10)
 left outer join security as c12
        on (c12.employeeId = a.employeeId and 
            c12.criteria_1 = c1.criteria_1 and 
            c12.criteria_2 = a.criteria_2 and
            c12.criteria_3 = a.criteria_3 and
            c12.criteria_4 = a.criteria_4 and
            c12.criteria_5 = a.criteria_5 and
            c12.criteria_6 = a.criteria_6 and
            c12.criteria_7 = a.criteria_7 and
            c12.criteria_8 = a.criteria_8 and
            c12.criteria_9 = a.criteria_9 and
            c12.criteria_10 = a.criteria_10 and
            c12.criteria_11 = a.criteria_11);

答案 1 :(得分:1)

如果您的security表结构类似于

Name                                      Null?    Type
----------------------------------------- -------- ----------------------------
EMPLOYEEID                                         VARCHAR2(9)
CRITERIA_1_VALID_VALUE                             VARCHAR2(15)
CRITERIA_2_VALID_VALUE                             VARCHAR2(15)
CRITERIA_3_VALID_VALUE                             VARCHAR2(15)

带数据

EMPLOYEEI CRITERIA_1_VALI CRITERIA_2_VALI CRITERIA_3_VALI
--------- --------------- --------------- ---------------
alex      crit 1a         crit 1a 2a      crit 1a 2a 3a
alex      crit 1a         crit 1a 2b      crit 1a 2b 3a
alex      crit 1a         crit 1a 2c      crit 1a 2c 3a
alex      crit 1a         crit 1a 2c      crit 1a 2c 3b
alex      crit 1b         crit 1b 2a      crit 1b 2a 3a
alex      crit 1b         crit 1b 2b      crit 1b 2b 3a
alex      crit 1b         crit 1b 2c      crit 1b 2c 3a
alex      crit 1c         crit 1c 2a      crit 1c 2a 3a

然后这会给你需要的结果吗?

create or replace type t_crit_values as table of varchar2(15)
/

show errors

create or replace function get_criteria(v_EmployeeID in varchar2,
    v_Level in number,
    v_Criteria_1 in varchar2 default ' ',
    v_Criteria_2 in varchar2 default ' ',
    v_Criteria_3 in varchar2 default ' ')
return t_crit_values as

    cursor c_values is
        select distinct(case v_Level
            when 1 then Criteria_1_valid_Value
            when 2 then Criteria_2_valid_Value
            when 3 then Criteria_3_valid_Value
        end) value
        from security
        where EmployeeID = v_EmployeeID
        and (v_Criteria_1 = ' ' or Criteria_1_valid_Value = v_Criteria_1)
        and (v_Criteria_2 = ' ' or Criteria_2_valid_Value = v_Criteria_2)
        and (v_Criteria_3 = ' ' or Criteria_3_valid_Value = v_Criteria_3);

    l_crit_values t_crit_values;
    i number;
begin
    l_crit_values := t_crit_values();

    for r_value in c_values loop
        l_crit_values.EXTEND;
        l_crit_values(l_crit_values.LAST) := r_value.value;
    end loop;

    return l_crit_values;
end;
/

show errors

然后调用该函数,每次传递所需的级别和所有更高级别(可能是' ')的选定值。像

这样的东西
// first level
select * from table(get_criteria('alex', 1));

COLUMN_VALUE
---------------
crit 1a
crit 1b
crit 1c

// second level with 'crit 1b' selected
select * from table(get_criteria('alex', 2, 'crit 1b'));

COLUMN_VALUE
---------------
crit 1b 2a
crit 1b 2b
crit 1b 2c

// second level with 'crit 1c' selected
select * from table(get_criteria('alex', 2, 'crit 1c'));

COLUMN_VALUE
---------------
crit 1c 2a

// third level with 'crit 1b' and 'crit 1b 2a' selected
select * from table(get_criteria('alex', 3, 'crit 1b', 'crit 1b 2a'));

COLUMN_VALUE
---------------
crit 1b 2a 3a

// third level with 'crit 1b' and 'all values' selected
select * from table(get_criteria('alex', 3, 'crit 1b', ' '));

COLUMN_VALUE
---------------
crit 1b 2a 3a
crit 1b 2b 3a
crit 1b 2c 3a

为简洁起见,我只走了三个级别,但很容易扩展。或者我不明白你想要做什么?

答案 2 :(得分:1)

假设你想要一个像这样的表只有3个标准吗?

id c1 c2 c3
0 a b c
1 a b空间
2个空格c
3空间空间
4个空格b c
5个空间b空间
6个空间c
7个空间空间

如果使用sqlloader创建这样的表,例如包含0到2 ^ 12 -1的id列,则将空格放入所有条件列中,然后可以使用算术更新它:
更新临时集c1 =(选择标准1 ...)其中mod(id,2)< 1;
更新临时集c2 =(选择标准2 ...)其中mod(id,4)< 2;
update temp set c3 =(select criteria3 ...)其中mod(id,8)< 4;

看起来像是一个奇怪的要求。

答案 3 :(得分:0)

我不完全理解您的要求,但我认为对标准2的查询将是:

select distinct Criteria2
from Security
where EmployeeID = :the_user
and Criteria1 = :Criteria1

如果用户必须在Criteria2之前输入Criteria1,或

select distinct Criteria2
from Security
where EmployeeID = :the_user
and (:Criteria1 is null or Criteria1 = :Criteria1)

否则?

答案 4 :(得分:0)

所以最终它确实归结为性能问题。我创建了一个表,它反向保存2 ^ 10个整数的二进制表示(litte-endian,如果你愿意的话)。

<强> DECBIN

decimal   binary
0         0000000000
1         1000000000
2         0100000000
...
1023      1111111111

然后我将笛卡尔连接到安全表并解码每个位以获得正确的值。

所以sql看起来像这样:

    SELECT DISTINCT
       t.employeeID,
       DECODE (SUBSTR (x.binary,  1, 1), 0, ' ', t.c1)  AS crit1,
       DECODE (SUBSTR (x.binary,  2, 1), 0, ' ', t.c2)  AS crit2,
       DECODE (SUBSTR (x.binary,  3, 1), 0, ' ', t.c3)  AS crit3,
       DECODE (SUBSTR (x.binary,  4, 1), 0, ' ', t.c4)  AS crit4,
       DECODE (SUBSTR (x.binary,  5, 1), 0, ' ', t.c5)  AS crit5,
       DECODE (SUBSTR (x.binary,  6, 1), 0, ' ', t.c6)  AS crit6,
       DECODE (SUBSTR (x.binary,  7, 1), 0, ' ', t.c7)  AS crit7,
       DECODE (SUBSTR (x.binary,  8, 1), 0, ' ', t.c8)  AS crit8,
       DECODE (SUBSTR (x.binary,  9, 1), 0, ' ', t.c9)  AS crit9,
       DECODE (SUBSTR (x.binary, 10, 1), 0, ' ', t.c10) AS crit10,
       DECODE (SUBSTR (x.binary, 10, 1), 0, 'Choose All',t.c11) AS crit10Descr
  FROM Security t, DECBIN x
 WHERE TO_NUMBER (x.decimal) BETWEEN 0 AND POWER (2, 10) - 1

速度提高了10倍。感谢@ JD_55让我以新的方式思考问题。