有条件地加入表没有子查询?

时间:2017-01-28 21:13:35

标签: sql oracle

在简化形式中,我们有两个表 - 事务表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)。父母可以简单地从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    |
+-------+------------+---------+-----------+

目前我们有一个视图可以做到这一点,但它正在使用子查询。 e.g:

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. 我们无法创建物化视图。
  4. 这是一些快速的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');
    

2 个答案:

答案 0 :(得分:0)

如果可以假设缺少的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

答案 1 :(得分:0)

这是需要考虑的事情。在您提供的小样本上,优化器成本为7,而原始查询为13。不过在你的大数据集上尝试一下。

根据您的Oracle版本(您应该始终包含您的问题),factored子查询声明(with子句)中的列别名可能有效,也可能无效;如果您在11.1或之前,则可能需要在因子子查询中移动它们。

我们的想法是首先进行左连接,然后“保存”它(这是一个因子子查询所做的事情)。然后选择item_desc不是null的所有行;并为null的{​​{1}}行进行进一步的连接。

顺便说一句,查找表中的item_desc可以item_desc吗?我没想到(在查询表中会不寻常);如果可以null,则可以轻松调整(将null添加到i.item_id子句,并使用它来确定来自{{1}的哪一行}转到with)的哪个分支。

j