Создайте представление, позволяющее добавлять условия в LEFT JOIN ON?

Я создаю базу данных (используя Oracle 12c), которая будет использоваться рядом клиентских приложений. Для упрощения я стараюсь сохранить как можно больше логики внутри базы данных, создавая различные представления, чтобы клиенты могли просто задать простой вопрос без более сложных конструкций, таких как JOIN или GROUP BY. Им нужно просто выполнить простой SELECT с некоторыми WHERE условиями из представления, и пусть представление сделает всю тяжелую работу.

Теперь моя проблема в том, что я хочу задать вопросы в форме

SELECT
    -- Some fields.
FROM a
LEFT JOIN b ON a.id = b.id AND b.type = x
LEFT JOIN c ON a.id = c.id AND c.type = y
LEFT JOIN d ON a.id = d.id AND d.type = z

где x, y и z вводятся клиентом. Я не хочу, чтобы клиентам приходилось создавать этот запрос. Я бы предпочел, чтобы они сделали что-то более похожее на это:

SELECT * FROM a_view WHERE b_type = x AND c_type = y AND d_type = z

Конечно, я мог бы создать по одному представлению для каждой возможной комбинации x, y и z, но тогда было бы много представлений. Есть ли лучший способ решить эту проблему, или я должен просто сдаться и позволить клиентам написать запрос с JOIN?


person Anders    schedule 13.11.2015    source источник
comment
Использование left join делает это довольно сложным. Я мог бы предложить вам задать другой вопрос с лучшими примерами таблиц и типов предложений where, которые вы хотите поддерживать. Может быть способ сделать это. Просто переключиться на inner join — одно из возможных решений.   -  person Gordon Linoff    schedule 13.11.2015
comment
Не могли бы вы посоветовать мне, что улучшит примеры? Я рассматривал возможность использования INNER JOIN, но поскольку я всегда хочу возвращать все строки из левой таблицы (a), думаю, мне нужен LEFT JOIN.   -  person Anders    schedule 13.11.2015
comment
В MSSQL это возможно с табличными функциями. Я не уверен в Oracle.   -  person Adish    schedule 13.11.2015
comment
Возможно, вы сможете делать то, что хотите, с функциями конвейера. См. ответ на этот вопрос: stackoverflow.com/questions/2059299/.   -  person Gordon Linoff    schedule 13.11.2015
comment
Я изучу функции конвейера и вернусь с отчетом. Выглядит многообещающе.   -  person Anders    schedule 13.11.2015
comment
Вы можете использовать where c.type is null or c.type = x   -  person a_horse_with_no_name    schedule 13.11.2015
comment
Что делать, если в таблице c есть мах для a, где type равно w. Тогда эта строка не будет включена в вопрос, поскольку c.type будет w, а не null или x. Мне нужно, чтобы каждая строка в a была включена в результат.   -  person Anders    schedule 13.11.2015


Ответы (2)


Это можно сделать, но у вас должен быть источник всех допустимых типов и предполагается, что пользователь/приложение будет запрашивать представление только для допустимого типа. Другими словами, предположим, что таблица T является вашей «таблицей типов», и все ваши таблицы B, C и D имеют (или могут иметь) FK, определенные на T.

В этом случае ваш вид опр. было бы:

CREATE OR REPLACE VIEW V AS
SELECT A.ID, BT.TYPE BTYPE, CT.TYPE CTYPE, DT.DTYPE DTYPE, ... 
FROM A 
CROSS JOIN T BT
CROSS JOIN T CT
CROSS JOIN T DT
LEFT JOIN B ON A.ID = B.ID AND B.TYPE = BT.TYPE
LEFT JOIN C ON A.ID = C.ID AND C.TYPE = CT.TYPE
LEFT JOIN D ON A.ID = D.ID AND D.TYPE = DT.TYPE;

Конечно, если бы в модели был фиксированный набор допустимых типов, вы могли бы заменить T на (SELECT 'X' TYPE FROM DUAL UNION ALL SELECT 'Y' FROM DUAL UNION ALL ...)

Единственная разница между:

SELECT
    -- Some fields.
FROM a
LEFT JOIN b ON a.id = b.id AND b.type = x
LEFT JOIN c ON a.id = c.id AND c.type = y
LEFT JOIN d ON a.id = d.id AND d.type = z

и

SELECT * FROM V WHERE B_TYPE = X AND C_TYPE = Y AND D_TYPE = Z

если бы X, Y или Z не были "действительными" типами (в этом случае определение представления не вернуло бы строк).

РЕДАКТИРОВАТЬ: Уточнение за комментарий; Я предполагал, что «тип» является общим доменом для таблиц B, C и D. Если B.type отличается от C.type (т. е. B.TYPE — это NUMERIC(9), а C.type — это VARCHAR(2) и D.TYPE имеет значение NUMERIC(1)), тогда перекрестные соединения должны ссылаться на каждый «набор допустимых значений типа» независимо:

CREATE OR REPLACE VIEW V AS
SELECT A.ID, BT.TYPE BTYPE, CT.TYPE CTYPE, DT.DTYPE DTYPE, ... 
FROM A 
CROSS JOIN (--SELECT ALL DISTINCT VALID B.TYPE VALUES--) BT
CROSS JOIN (--SELECT ALL DISTINCT VALID C.TYPE VALUES--) CT
CROSS JOIN (--SELECT ALL DISTINCT VALID D.TYPE VALUES--) DT
LEFT JOIN B ON A.ID = B.ID AND B.TYPE = BT.TYPE
LEFT JOIN C ON A.ID = C.ID AND C.TYPE = CT.TYPE
LEFT JOIN D ON A.ID = D.ID AND D.TYPE = DT.TYPE;

Тем не менее, у вас действительно есть такое же ограничение: встроенное в определение представления. должен быть некоторым конечным источником «всех допустимых типов B», «всех допустимых типов C» и «всех допустимых типов D». За исключением этого, это невозможно в чистом SQL (на самом деле, в терминах чистого SQL это становится неразрешимой проблемой — представление, которое поддерживает фильтрацию по любой мыслимой комбинации значений без фильтров, должно возвращать все мыслимые комбинации значений. ..)

person KevinKirkpatrick    schedule 13.11.2015
comment
Спасибо за ваш ответ. Это очень интересная альтернатива. Как вы упомянули, ограничение заключается в том, что все типы должны быть дискретными. Если бы один из них был, скажем, NUMBER(9), все сломалось бы. - person Anders; 13.11.2015
comment
Вовсе нет - если типы B отличаются от типов C и отличаются от типов D, тогда вы должны использовать CROSS JOIN <All valid B types> BT вместо CROSS JOIN T BT и так далее. Однако исходное ограничение по-прежнему применяется: если типы B, типы C и типы D относятся к 3 различным наборам значений, вам все равно потребуется некоторый источник допустимых типов B, некоторый источник допустимых типов C и некоторый источник допустимых типов D. В противном случае, однако, ничто не говорит, что типы должны быть одинаковыми (это было предположение, которое я, вероятно, не должен был делать) - person KevinKirkpatrick; 13.11.2015
comment
Извините, неясно выражаюсь. Я пытался сказать следующее: если есть много-много разных значений для одного из типов (скажем, тысяча), то представление вернет абсурдно большое количество строк, и я предполагаю, что возникнут проблемы с производительностью? - person Anders; 13.11.2015
comment
Нет - до тех пор, пока представление фильтруется дискретными спецификациями, из которых желательны B_TYPE, C_TYPE и D_TYPE, эти спецификации будут помещены в определение представления... другими словами, никакое фактическое декартово произведение не будет выполняться, если только пользователь не укажет, какую комбинацию параметров он хочет вернуть - person KevinKirkpatrick; 14.11.2015

Вероятно, вы можете использовать «синтаксис соединения старого стиля»:

SELECT
    -- Some fields.
FROM a, b, c, d
WHERE a.id = b.id(+) AND b.type(+) = x
  AND a.id = c.id(+) AND c.type(+) = y
  AND a.id = d.id(+) AND d.type(+) = z

Например:

SQL> with
     t  as (select rownum r from dual connect by level < 6),
     t1 as (select rownum r from dual connect by level < 5),
     t2 as (select rownum r from dual connect by level < 4)
select *
  from t, t1, t2
 where t.r = t1.r(+) and t1.r(+) = 3
   and t.r = t2.r(+) and t2.r(+) = 2
order by 1, 2, 3;

     R      R          R
---------- ---------- ----------
     1
     2             2
     3      3
     4
     5
person Dmitriy    schedule 13.11.2015
comment
Спасибо за ваш ответ. Моя цель здесь заключалась бы в том, чтобы клиенты знали только об одном представлении и не беспокоились о нескольких таблицах и их отношении друг к другу. Если я правильно вас понял, клиент все равно должен знать обо всех таблицах. - person Anders; 13.11.2015