Oracle使用变量字段

时间:2017-02-17 16:24:18

标签: sql oracle join

我正试图获得每个月收取笼子的租金率。以下是如何获得它的逻辑:

  1. 如果在cage表中定义了费率,请使用该表的外键链接到rate表。
  2. 如果在rack表中定义了费率,请使用该表的外键链接到rate表。
  3. 如果在room表中定义了费率,请使用该表的外键链接到rate表。
  4. 如果以上所有内容都false返回$0.00免费租金)。
  5. 所以这里有一些表格供您直观地了解我目前的情况。

    id    cage_number     rate_id     rack_id    room_id
    ----------------------------------------------------
    1     FG-12           600         1          1
    2     FG-13           600         1          1
    3     FG-14           NULL        2          1
    4     FG-15           NULL        2          3
    

    id    rack_number     rate_id
    -----------------------------
    1     BN-806          700
    2     BN-971          NULL
    

    房间

    id    room_number     rate_id
    -----------------------------
    1     UM-100          800
    2     UM-150          900
    3     UM-200          NULL
    

    价格

    id   rate_id    effective_on    rate_value
    ------------------------------------------
    1    600        01-Apr-2015     0.67
    2    600        01-Oct-2016     0.80
    3    700        01-Jan-2016     0.56
    4    800        01-Jul-2012     0.61
    5    800        01-Dec-2015     0.85
    6    900        01-Feb-2015     0.75
    7    900        01-Mar-2015     0.79
    8    900        01-Apr-2015     0.90
    

    因为您可以看到每个rate_id有多行,因为价格会不时变化。我已经通过查看rate_id字段来查询每个effective_on的正确率:

    SELECT *
    FROM rates r
    WHERE r.id = (
      SELECT MAX(rr.id) AS curr
      FROM rates rr
      WHERE rr.rent_id = r.rent_id
        AND TRUNC(rr.effective_on) <= TRUNC(sysdate)
    )
    

    让我们回到我的主要问题。我需要使用位于3个不同表格的rate加入rate_id表。

    当前解决方案

    我目前的方法是使用3个不同的别名加入rate表3次,但由于速率可能有多行,所有3个实例每个都可能有多行导致大量不需要的行行。

    FROM cages c
      LEFT JOIN racks rk ON rk.id = c.rack_id
      LEFT JOIN rooms rm ON rm.id = c.room_id
      LEFT JOIN rates cr ON cr.rate_id = c.rate_id
      LEFT JOIN rates rkr ON rkr.rate_id = rk.rate_id
      LEFT JOIN rates rmr ON rmr.rate_id = rm.rate_id
    

    试图(但悲惨地失败)

    我在考虑一个解决方案,我只需要加入rate表一次来解决这个问题,但是当我尝试它时 - 它失败了(正如预期的那样)。

    FROM cages c
      LEFT JOIN racks rk ON rk.id = c.rack_id
      LEFT JOIN rooms rm ON rm.id = c.room_id
      CASE
        WHEN c.rate_id IS NOT NULL
          THEN LEFT JOIN rates r ON r.rate_id = c.rate_id
        WHEN rk.rate_id IS NOT NULL
          THEN LEFT JOIN rates r ON r.rate_id = rk.rate_id
        WHEN rm.rate_id IS NOT NULL
          THEN LEFT JOIN rates r ON r.rate_id = rm.rate_id
      END
    

2 个答案:

答案 0 :(得分:1)

您不能使用案例表达式来决定使用哪个加入条件,但您可以在条件的右侧使用案例表达式来决定您的&# 39;重新尝试将费率表的ID与:

相匹配
FROM cages c
  LEFT JOIN racks rk ON rk.id = c.rack_id
  LEFT JOIN rooms rm ON rm.id = c.room_id
  LEFT JOIN rates r ON r.rate_id =
  CASE
    WHEN c.rate_id IS NOT NULL THEN c.rate_id
    WHEN rk.rate_id IS NOT NULL THEN rk.rate_id
    WHEN rm.rate_id IS NOT NULL THEN rm.rate_id
  END

但是对于费率表上的外连接条件使用coalesce()更简单:

  LEFT JOIN rates r ON r.rate_id = COALESCE(c.rate_id, rk.rate_id, rm.rate_id)

或者更有用的是针对费率表的子查询,该表查找每个ID的最新费率:

select cages.cage_number,
  rates.rate_id as rate_id,
  nvl(rates.rate_value, 0)
from cages
left join racks on racks.id = cages.rack_id
left join rooms on rooms.id = cages.room_id
left join (
  select rate_id,
    max(rate_value) keep (dense_rank last order by effective_on) as rate_value
  from rates
  where effective_on <= trunc(sysdate)
  group by rate_id
) rates on rates.rate_id = coalesce(cages.rate_id, racks.rate_id, rooms.rate_id);

CAGE_    RATE_ID COALESCE(RATES.RATE_VALUE,0)
----- ---------- ----------------------------
FG-13        600                           .8
FG-12        600                           .8
FG-14        800                          .85
FG-15                                       0

子查询忽略了将来开始的速度,正如您已经在做的那样;并使用keep dense_rank last to avoid the self-join

然后将该子查询用作内联视图(为简单起见,别名为&#39; rate&#39;)。加入条件使用coalesce来加入笼子的费率ID(如果已设置);那么机架ID;然后是房间ID。最后,如果根本没有匹配,那么另一个合并将最终选择的值设置为零。

正如@mathguy指出的那样,这最终可能会做一些不必要的工作 - 如果笼子里有一个费率ID,那么看看那个笼子的其他桌子是没有意义的。取决于数据的选择性可能无关紧要,但如果它确实影响性能,您可以考虑修改外连接条件以消除其中的一些:

from cages
left join racks on racks.id = cages.rack_id
  and cages.rate_id is null
left join rooms on rooms.id = cages.room_id
  and cages.rate_id is null
  and racks.rate_id is null
left join (
  ...

答案 1 :(得分:1)

这是为每个笼子获得正确rate_id的不同方法。根据您在不同表中的null个数量,它可能比在整个表上执行左连接更有效(更快)。我们的想法是在rate_id表不为空时从cages表中收集rates,并且只有当它为空才能加入到机架表时,并且只有当它不起作用时才加入只有剩余的行到房间表。

我没有展示如何将此“连接”到with prep ( id, cage_number, rate_id, room_id ) as ( select c.id, c.cage_number, rk.rate_id, c.room_id from cages c left outer join racks rk on c.rack_id = rk.id where c.rate_id is null ) select id, cage_number, rate_id from cages where rate_id is not null union all select id, cage_number, rate_id from prep where rate_id is not null union all select p.id, p.cage_number, rm.rate_id from prep p left outer join rooms rm on p.room_id = rm.id where p.rate_id is null ; 表格 - 在Alex的回答中这两种方法都可以。 (我会再次使用第二个版本来提高效率。)

clickable