вложенный зависимый вопрос sql - PullRequest
0 голосов
/ 11 февраля 2010

Я пытаюсь реализовать внешний интерфейс для решения для отчетности, которое зависит от безопасности. Пользователь может выбрать из 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. На каждом уровне опция для выбора «Все значения», которая представлена ​​пробелом для поиска. Поэтому мне нужно представление в справочной таблице / представлении, которое учитывает наличие пробела на одном или нескольких уровнях.

Мне трудно найти способ построить представление / таблицу поиска для каждого поля Criteria с использованием sql, не прибегая к жесткому кодированию.

Например, для построения поиска по критерию 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: Спасибо за вклад всех. Я получил корректное соединение, используя соединения, как в примере proc из @Alex Poole ниже:

and (v_Criteria_1 = ' ' or Criteria_1_valid_Value = v_Criteria_1)

Мне не хватало v_Criteria_1 = ' ' or.

Итак, теперь таблицы загружены правильно (достаточно). Это превращается в упражнение по настройке / оптимизации. Я собираюсь посмотреть на процесс от @Alex Poole и арифметический подход @ JD_55, который, я думаю, может быть очень быстрым.

Ответы [ 5 ]

1 голос
/ 12 февраля 2010

Скажите, что для таблицы, подобной этой, нужно всего 3 критерия?

id c1 c2 c3
0 а б в
1 a b пробел
2 пробел с
3 пробел
4 место b c
5 пробел б пробел
6 пробел с
7 космическое пространство

Если вы создаете такую ​​таблицу с использованием sqlloader, например, включающей столбец id от 0 до 2 ^ 12 -1, вставляете пробелы во все столбцы критериев, то вы можете обновить ее, используя арифметику:
обновить временный набор c1 = (выберите критерий1 ...) где mod (id, 2) <1; <br> обновить временную шкалу c2 = (выберите критерии2 ...) где mod (id, 4) <2; <br> обновить временную шкалу c3 = (выбрать критерии3 ...) где mod (id, 8) <4; </p>

Кажется странным требованием.

1 голос
/ 12 февраля 2010

Рассмотрим серию левого внешнего собственного объединения с каждым элементом критерия, зависящим от значений предыдущих критериев. Вы можете использовать функцию 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 голос
/ 12 февраля 2010

Если ваша 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

Для краткости я прошел только три уровня, но его было бы легко расширить. Или я не понял, что вы пытаетесь сделать?

0 голосов
/ 03 марта 2010

Итак, в итоге все сводилось к проблеме производительности.Я создал таблицу, которая содержала двоичное представление 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, за то, что заставили меня подумать о проблеме по-новому.

0 голосов
/ 11 февраля 2010

Я не полностью понимаю ваши требования, но я бы подумал, что запрос для критерия2 будет:

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

, если пользователь должен ввести Критерии1 до Критерии2, или

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

в противном случае?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...