【问题标题】:nested dependent sql question嵌套依赖sql问题
【发布时间】:2010-02-11 17:06:04
【问题描述】:

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

所以页面上的标准选择(每个都是一个下拉菜单)看起来像这样:

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

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

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

并且每个员工在此表中将有一个或(许多)多行。把它想象成一棵扁平的树,以 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 个联合,坦率地说,这很臭。

我已经尝试为每个级别批量构建一个表,在每个级别之后提交,然后使用前一个表连接到安全表来构建下一个表,每个表中都有一个 UNION,但我似乎无法获得连接以与空间正常工作。

我不知道我是想多了还是完全错过了一些东西,但我觉得必须有一个更简单的解决方案。

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

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

and (v_Criteria_1 = ' ' or Criteria_1_valid_Value = v_Criteria_1)

我错过了v_Criteria_1 = ' ' or

所以我现在已经正确(足够)加载了表格。这变成了调整/优化练习。我要看看@Alex Poole 的过程和@JD_55 的算法方法,我认为这可能很快。

【问题讨论】:

  • 您能否发布一个简短的示例,显示 Criteria1 的值选择如何影响 Criteria2Criteria3 的可用值范围?这是我希望 SO 提供白板工具的情况之一。
  • 我同意。基本上它是一棵扁平树,因此 c1 会影响 c2 中出现的值,而在 c2 中选择单个值会影响 c3 的可用值,等等...

标签: sql oracle join


【解决方案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

为简洁起见,我只介绍了三个级别,但扩展起来很容易。还是我不明白你要做什么?

【讨论】:

  • 现在解析这个。有趣的方法。您已经获得了正确的 Security 表结构。唯一的区别是该值不会在每一列中重复 - 相反每一列都是一个键。所以第一行看起来像这样: EMPLOYEEI CRITERIA_1_VALI CRITERIA_2_VALI CRITERIA_3_VALI - - - - - - - - - - - - - - - - - - - - - - - - - - - - alex crit 1a crit 2a crit 3a Not一个大问题,只是为了清晰。
  • 是的,我只是这样做了,这样您就可以在最终输出中更轻松地跟踪它。如果您也需要,您还可以加入结果以获取描述(如果这些是用于选择中的选项标签,我认为这就是我的阅读方式!)
【解决方案2】:

假设您想要这样的表格只有 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 列,则在所有条件列中添加空格,然后您可以使用算术更新它:
update temp set c1 = (select criteria1 ...) where mod(id,2) update temp set c2 = (select criteria2 ...) where mod(id,4) update temp set c3 = (select criteria3 ...) where mod(id,8)

这似乎是一个奇怪的要求。

【讨论】:

  • 啊!我知道有一些方法可以使用算术来做到这一点。我要调查这件事。谢谢!顺便说一句,你对我需要生成的表的解释是正确的。
【解决方案3】:

考虑一系列左外自连接,每个条件项取决于先前条件的值。当左连接产生空结果时,您可以使用 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);

【讨论】:

    【解决方案4】:

    我不完全理解您的要求,但我原以为条件 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)
    

    不然呢?

    【讨论】:

    • 我明白你的意思,但由于 ERP 系统页面构建的拜占庭功能,我需要查找每个字段以包含上述所有字段的键。这将允许它级联选择标准。我的问题是其中一些“键”将包含空格。
    • 好的,我知道我没有理解一些东西!
    【解决方案5】:

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

    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 让我以新的方式思考这个问题。

    【讨论】:

      猜你喜欢
      • 2019-08-27
      • 2016-08-03
      • 1970-01-01
      • 2020-02-11
      • 2014-10-02
      • 2014-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多