简化冗余左连接
Posted
技术标签:
【中文标题】简化冗余左连接【英文标题】:Simplify a redundant left join 【发布时间】:2015-12-15 13:57:49 【问题描述】:使用 Oracle,我希望执行以下查询,但我想知道是否有更“智能”的方式来执行此操作。
Select * from Sales Sales1
left join Sales Sales2 on Sales2.val = Sales1.val
left join Sales Sales3 on Sales3.val = Sales2.val
left join Sales Sales4 on Sales4.val = Sales3.val
left join Sales Sales5 on Sales5.val = Sales4.val
...
这是我的示例数据的样子
customer number | acct | start balance | open date | prev account
a 1 100 01-01-15 b-1
b 1 80 03-04-14
c 2 200 04-11-14 c-1
c 1 150 06-12-15
d 1 600 08-16-15
e 3 400 12-19-15 e-2
e 2 150 10-21-14 e-1
e 1 100 01-18-13
结果集如下所示:
Customer | start | open | prevStart_01 | prevOpen_01 | prevStart_02 | prevOpen_02
a-1 | 100| 01-01-15| 80 | 03-04-14 | |
c-2 | 200| 04-11-14| 150 | 06-11-14 | |
e-3 | 400| 12-19-15| 150 | 10-21-14 | 100| 01-18-13
如您所见,我需要根据结果继续加入另一条销售记录,并且我需要继续这样做,直到返回一个空的结果集。我当前的场景是运行查询并查看 sales5、sales6、sales7 等中是否有值。
【问题讨论】:
@xQbert 我需要每个连接中的几列。 查看分层查询后可能无济于事。然而,取消数据透视可能。但是,我需要知道相关字段和示例数据的表结构以及预期的结果。这是example unpivot,它可能对您有帮助,也可能对您没有帮助,因为这个问题对预期结果仍然有点模糊。我看到的问题是数据没有标准化,这使得查询需要额外的连接。但是为什么你首先需要连接呢?提供样本数据预期结果。 @xQbert 我添加了一些示例数据和我正在寻找的内容,但我认为您的答案与我正在寻找的内容接近。 啊,那么您是在动态枢轴之后,而不是未枢轴。您想在 prevstart 和 prevOpen 上旋转数据。这是一个示例:***.com/questions/11987067/… @xQbert 这里的 IN 子句会是什么样子?匹配列是客户编号和帐户的串联。 【参考方案1】:每当您必须自行加入未知次数时,您应该考虑CONNECT BY
。您在这里的特殊需求并不是那么简单,但CONNECT BY
仍然是解决方案的关键要素。
在下面的 SQL 中,mockup_data
子因素只是为了给我一些数据。你会使用你的实际表。
这个想法是您在数据中搜索“根”——不是任何其他记录的prev_account
的记录。然后,您从这些和CONNECT BY
开始获取他们以前的所有帐户,数量不限。然后你PIVOT
将它们全部放入列中。
有一件事——Oracle SQL 语句不能有任意(数据驱动的)列数。解析 SQL 时必须知道编号。因此,在您的 PIVOT
子句中,您需要指定您将支持的最大“级别”数,以便 Oracle 知道结果集可以包含多少列。
这是 SQL。
WITH
mockup_data as (
SELECT
'a' customer_Number, 1 acct, 100 start_balance, to_date('01-01-15','MM-DD-YY') open_date, 'b-1' prev_account from dual union all
SELECT 'b' ,1, 80, to_date('03-04-14','MM-DD-YY'), null from dual union all
SELECT 'c' ,2, 200, to_date('04-11-14','MM-DD-YY'), 'c-1' from dual union all
SELECT 'c' ,1, 150, to_date('06-12-15','MM-DD-YY'), null from dual union all
SELECT 'd' ,1, 600, to_date('08-16-15','MM-DD-YY'), null from dual union all
SELECT 'e' ,3, 400, to_date('12-19-15','MM-DD-YY'), 'e-2' from dual union all
SELECT 'e' ,2, 150, to_date('10-21-14','MM-DD-YY'), 'e-1' from dual union all
SELECT 'e' ,1, 100, to_date('01-18-13','MM-DD-YY'), null from dual ),
data_with_roots AS
(SELECT d.*,
CASE
WHEN (SELECT COUNT (*)
FROM mockup_data d2
WHERE d2.prev_account = d.customer_number || '-' || d.acct) = 0 THEN
'Y'
ELSE
'N'
END
is_root
FROM mockup_data d),
hierarchy AS
(SELECT CONNECT_BY_ROOT (customer_number) customer_number,
CONNECT_BY_ROOT (acct) acct,
CONNECT_BY_ROOT (start_balance) start_balance,
CONNECT_BY_ROOT (open_date) open_date,
start_balance prev_start_balance,
open_date prev_open_date,
LEVEL - 1 lvl
FROM data_with_roots d
CONNECT BY customer_number || '-' || acct = PRIOR prev_account
START WITH is_root = 'Y'),
previous_only AS
(SELECT *
FROM hierarchy
WHERE lvl >= 1)
SELECT *
FROM previous_only PIVOT (MAX (prev_start_balance) AS prev_start, MAX (prev_open_date) AS prev_open
FOR lvl
IN (1 AS "01", 2 AS "02", 3 AS "03", 4 AS "04", 5 AS "05" -- etc... as many levels as you need to support
));
【讨论】:
一个想法——在另一个方向连接可能会更好(即START WITH prev_account IS NULL
。这将允许您使用CONNECT_BY_ISLEAF
来确定我在上面所说的“根”。它可能会更有效。你可以玩的东西......
谢谢。这真的很彻底。是否可以在 pivot 子句中使用 MAX(acct) 来驱动结果列的数量,或者是否必须列出它们?
如果您想在单个查询中执行此操作而不使用动态 SQL,则需要将它们列出。这对于您的客户端应用程序通常也很重要——知道它将获得多少列以及它们被称为什么。如果您真的不喜欢它,您可以跳过PIVOT
并使用LISTAGG
将所有内容集中到一个大列中(由管道或其他任何内容分隔)并让您的客户端应用程序对其进行解析。以上是关于简化冗余左连接的主要内容,如果未能解决你的问题,请参考以下文章
Hive sql中的 各种join(内连接左外连接右外连接满外连接)