创建一个视图,让您向 LEFT JOIN ... ON? 添加条件?

Posted

技术标签:

【中文标题】创建一个视图,让您向 LEFT JOIN ... ON? 添加条件?【英文标题】:Create a view that lets you add conditions to the LEFT JOIN ... ON? 【发布时间】:2015-11-13 15:51:38 【问题描述】:

我正在创建一个可供许多客户端应用程序使用的数据库(使用 Oracle 12c)。为了简化,我尝试通过创建不同的视图将尽可能多的逻辑保留在数据库中,这样客户就可以只提出简单的问题,而无需像JOINGROUP 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

其中xyz 是从客户端输入的。我不希望客户必须构建该查询。我宁愿让他们做一些与此更相似的事情:

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

当然,我可以为xyz 的每一种可能组合创建一个视图,但这将是很多视图。有没有更好的方法来解决这个问题,还是我应该放弃让客户用JOIN写查询?

【问题讨论】:

left join 的使用使这变得相当困难。我可能会建议您用更好的表格示例和您想要支持的where 子句的类型提出另一个问题。可能有办法做到这一点。只是切换到inner join 是一种可能的解决方案。 你能告诉我什么会使这些例子更好吗?我考虑过使用INNER JOIN,但由于我总是想返回左表(a)中的所有行,我想我需要一个LEFT JOIN 在 MSSQL 中,这可以通过表值函数实现。我不确定甲骨文。 您可以使用管道功能做您想做的事情。请参阅此问题的答案:***.com/questions/2059299/…。 我将研究管道功能并返回报告。看起来很有希望。 【参考方案1】:

这可以做到,但您必须拥有所有有效类型的来源,并假设用户/应用程序只会查询有效类型的视图。换句话说,假设表 T 是您的“类型表”,并且您的所有 B、C 和 D 表都在 T 上定义了(或可能有)FK。

在这种情况下,您的视图定义。应该是:

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;

也就是说,你确实有同样的限制:内置在视图def中。必须是“所有有效的 B 类型”、“所有有效的 C 类型”和“所有有效的 D 类型”的某个有限源。除此之外,这在纯 SQL 中是不可行的(事实上,就纯 SQL 而言,它成为一个棘手的问题 - 一个支持通过任何可能的值组合进行过滤的视图,没有过滤器,应该返回所有可能的值组合。 ..)

【讨论】:

感谢您的回答。这是一个非常有趣的选择。正如您所提到的,限制是所有类型都必须是离散的。如果其中一个人说NUMBER(9),事情就会崩溃。 一点也不——如果 B 类型不同于 C 类型并且不同于 D 类型,那么您将使用 CROSS JOIN <All valid B types> BT 而不是 CROSS JOIN T BT 等等。但原始限制仍然适用:如果 B 类型、C 类型和 D 类型来自 3 组不同的值,您仍然需要 一些 有效 B 类型的来源,一些有效 C 类型的来源,以及一些有效 D 类型的来源。否则,没有什么说类型必须相同(这是我可能不应该做出的假设) 对不起,我表达不清楚。我想说的是:如果其中一种类型有很多很多不同的值(比如一千个),那么视图将返回大量的行,我想会有性能问题吗?跨度> 不——只要视图被需要 B_TYPE、C_TYPE 和 D_TYPE 的离散规范过滤,那么这些规范将被推送到视图定义中......换句话说,没有 将执行实际的笛卡尔积,除非用户没有指定他们想要返回的参数组合【参考方案2】:

或许,您可以使用“旧式连接语法”:

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

【讨论】:

感谢您的回答。我的目标是客户只需要了解一个视图,而不必担心多个表及其相互关系。如果我理解正确,客户仍然需要了解所有表格。

以上是关于创建一个视图,让您向 LEFT JOIN ... ON? 添加条件?的主要内容,如果未能解决你的问题,请参考以下文章

ORACLE:使用 LEFT JOIN 时物化视图不起作用

SQL Server 在视图查询中将 LEFT JOIN 替换为 LEFT OUTER JOIN

Left Join 横向和数组聚合

mysql left join 可以不写on吗

MySQL 的 2 个字段上的 SQL LEFT-JOIN

如何使用 $lookup MongoDB 进行 LEFT JOIN