繁体   English   中英

有条件地联接表而没有子查询?

[英]Join Tables Conditionally Without a Subquery?

以简化形式,我们有两个表-事务表TR和查找表ITEM:

表项目:

+---------+-----------+
| ITEM_ID | ITEM_DESC |
+---------+-----------+
| AAA     | parent    |
| AAA111  | child abc |
| AAA222  | child xyz |
+---------+-----------+

表TR:

+-------+------------+
| TR_ID | TR_ITEM_ID |
+-------+------------+
| 1     | AAA        |
| 2     | AAA111     |
| 3     | AAA222     |
| 4     | AAA333     |
| 5     | AAA444     |
+-------+------------+

当我们连接这两个表时,如果查找表中不存在TR_ITEM_ID (例如,对于AAA333AAA444 ),则必须在“父”项(即AAA )上AAA444该行。 家长可以简单地从ID的前三个字母中得出。 因此,理想的结果应该是这样的:

+-------+------------+---------+-----------+
| TR_ID | TR_ITEM_ID | ITEM_ID | ITEM_DESC |
+-------+------------+---------+-----------+
| 1     | AAA        | AAA     | parent    |
| 2     | AAA111     | AAA111  | child abc |
| 3     | AAA222     | AAA222  | child xyz |
| 4     | AAA333     | AAA     | parent    |
| 5     | AAA444     | AAA     | parent    |
+-------+------------+---------+-----------+

当前,我们有一个视图可以执行此操作,但是它正在使用子查询。 例如:

select * from (
  select TR.*,
    (select ITEM.ITEM_ID from ITEM where TR.TR_ITEM_ID = ITEM.ITEM_ID) CHILD_LOOKUP_TYPE,
    (select ITEM.ITEM_ID from ITEM where substr(TR.TR_ITEM_ID,1,3) = ITEM.ITEM_ID) PARENT_LOOKUP_TYPE
  from TR
) f left outer join ITEM on ITEM.ITEM_ID =
   case
     when f.CHILD_LOOKUP_TYPE is not null then f.CHILD_LOOKUP_TYPE
     when f.PARENT_LOOKUP_TYPE is not null then f.PARENT_LOOKUP_TYPE
   end
order by TR_ITEM_ID;

问题是,如果我们没有子查询,则视图执行的速度会更快(实际视图中还有其他联接,但我们总共估计它的运行速度会快近10倍)。 所以问题是,有没有办法在没有子查询的情况下重写以上视图? 还是有其他建议可以使加入效率更高?

如果有一些好的建议,请遵循以下限制条件:

  1. 我们无法在事务表中存储“已解决”的ITEM_ID ,因为我们无法修改客户数据库中的历史数据。
  2. 即使可以,查找表也会不时更改(例如,它们可能添加项AAA333 ),因此“已解析”值将变为无效。
  3. 我们无法创建实例化视图。

这是一些快速的SQL:

CREATE TABLE ITEM (
  ITEM_ID VARCHAR2(20 BYTE), 
  ITEM_DESC VARCHAR2(20 BYTE)
);
Insert into ITEM (ITEM_ID,ITEM_DESC) values ('AAA','parent');
Insert into ITEM (ITEM_ID,ITEM_DESC) values ('AAA111','child abc');
Insert into ITEM (ITEM_ID,ITEM_DESC) values ('AAA222','child xyz');

CREATE TABLE TR (
  TR_ID NUMBER,
  TR_ITEM_ID VARCHAR2(20 BYTE)
);
Insert into TR (TR_ID,TR_ITEM_ID) values (1,'AAA');
Insert into TR (TR_ID,TR_ITEM_ID) values (2,'AAA111');
Insert into TR (TR_ID,TR_ITEM_ID) values (3,'AAA222');
Insert into TR (TR_ID,TR_ITEM_ID) values (4,'AAA333');
Insert into TR (TR_ID,TR_ITEM_ID) values (5,'AAA444');

如果可以假设缺少的item_id的item_desc应该始终是parent ,那么这将是带有left join的简单查询。

select 
 tr.tr_id
,tr.tr_item_id
,coalesce(i.item_id,substr(tr.tr_item_id,1,3)) as item_id
,coalesce(i.item_desc,(select item_desc from item where substring(tr.tr_item_id,1,3)=item_id)) as item_desc
from tr
left join item i on tr.tr_item_id=i.item_id

这是要考虑的事情。 在您提供的小样本中,优化器成本为7,而原始查询为13。 不过,请对大型数据集进行尝试。

根据您的Oracle版本(应始终包含在问题中),分解式子查询声明( with子句)中的列别名可能会起作用,也可能不会起作用; 如果您使用的是11.1或更早版本,则可能需要在分解后的子查询中移动它们。

这个想法是先做一个左连接,然后“保存”它(这是分解子查询的作用)。 然后选择item_desc不为null所有行; item_descnull的行做进一步的item_desc

顺便说一句,在查询表中item_desc可以为null吗? 我以为不是(在查找表中这是不寻常的)。 如果它可以null ,则可以很容易地对其进行修改(将i.item_id添加到with子句中,并使用它来确定j哪一行去到并union all哪个分支union all )。

with j ( tr_id, tr_item_id, parent_lookup_type, item_desc ) as (
         select t.tr_id, t.tr_item_id, substr(t.tr_item_id, 1, 3), i.item_desc
         from   tr t left outer join item i on t.tr_item_id = i.item_id
       )
select  tr_id, tr_item_id, tr_item_id as child_lookup_type, parent_lookup_type,
        tr_item_id as item_id, item_desc
  from  j
  where j.item_desc is not null
union all
select  j.tr_id, j.tr_item_id, null, j.parent_lookup_type, j.parent_lookup_type,
        i.item_desc
  from  j left outer join item i on j.parent_lookup_type = i.item_id
  where j.item_desc is null
;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM