当访问外部表别名时,如何在SELECT的CASE中使用相关子查询?

时间:2019-03-01 18:55:20

标签: sql oracle

我正在使用Oracle12。我想出了一个简单的示例来尝试传达我所面临的问题,因为实际查询太复杂而无法在此处显示。这个简单的示例包含与复杂查询相同的条件和逻辑。我希望为主SELECT中的WHEN中的子查询找到一种访问表别名的方法,该别名是从与主SELECT处于同一级别的内部联接定义的。我尝试了此版本,但失败,并带有“ o”“表或视图不存在”:

 SELECT emp.employeeid, emp.firstname
 CASE 
 WHEN (SELECT count(*) FROM o WHERE o.shipperid <= 15) > 20 THEN 'Yes'
 WHEN (SELECT count(*) FROM o WHERE o.shipperid <= 15) > 10 THEN 'Almost'
 ELSE 'No'
 END AS "Quota Met"
 FROM Employee emp
 INNER JOIN 
 (SELECT employeeid, shipperid FROM Orders 
  WHERE orderdate > sysdate - 30) o
  ON o.employeeid = emp.id
 WHERE emp.zipcode = 22151;

无法使上面的INNER JOIN版本正常工作,到目前为止,我最好的解决方法是使用如下所示的WITH功能定义SELECT,但是在实际查询中,WITH块需要几分钟才能完成,因为无法执行将其限制为与Employee.employeeid匹配,就像上面的INNER JOIN版本尝试一样。

 WITH MyOrders AS
 (SELECT employeeid, shipperid FROM Orders 
  WHERE orderdate > sysdate - 30
 )
 SELECT emp.id, emp.name, o
  CASE 
   WHEN (SELECT COUNT(*) FROM MyOrders o WHERE o.shipperid <= 15) > 20 THEN 'Yes'
   WHEN (SELECT COUNT(*) FROM MyOrders o WHERE o.shipperid <= 15) > 10 THEN 'Almost'
  ELSE 'No'
  END AS "Quota Met"
 FROM Employee emp
 WHERE emp.zipcode = 22151;

这个WITH版本真的是最好的解决方案吗?有什么方法可以重写第一个INNER JOIN版本,以便CASE WHEN中的子查询可以访问联接的表别名?

2 个答案:

答案 0 :(得分:2)

您实际上并不需要子查询;如果将o.shipperid <= 15检查移到内联视图中,则可以只计算联接中的匹配项。我已将您的内部联接更改为外部联接,因此您将看到零计数为“否”-否则将没有匹配项,并且根本不会显示该员工。

SELECT emp.employeeid, emp.firstname,
   CASE 
     WHEN count(o.shipperid) > 20 THEN 'Yes'
     WHEN count(o.shipperid) > 10 THEN 'Almost'
     ELSE 'No'
   END AS "Quota Met"
FROM Employee emp
LEFT JOIN (
  SELECT employeeid, shipperid
  FROM Orders 
  WHERE orderdate > sysdate - 30
  AND o.shipperid <= 15
) o
ON o.employeeid = emp.id
WHERE emp.zipcode = 22151
GROUP BY emp.employeeid, emp.firstname;

由于这正在聚合,因此您也需要group by子句;对于您实际,更复杂的查询而言,这可能会带来更多问题。

如果由于正在使用o中其他未满足的数据而无法在更复杂的查询中移动该条件,则可以将条件聚合与更多的case表达式一起使用:

SELECT emp.employeeid, emp.firstname,
   CASE 
     WHEN count(CASE WHEN o.shipperid <= 15 THEN o.shipperid END) > 20 THEN 'Yes'
     WHEN count(CASE WHEN o.shipperid <= 15 THEN o.shipperid END) > 10 THEN 'Almost'
     ELSE 'No'
   END AS "Quota Met"
FROM Employee emp
LEFT JOIN (
  SELECT employeeid, shipperid
  FROM Orders 
  WHERE orderdate > sysdate - 30
) o
ON o.employeeid = emp.id
WHERE emp.zipcode = 22151
GROUP BY emp.employeeid, emp.firstname;

您可能仍然希望将其作为外部联接,但如果不只是将其更改回内部联接。

答案 1 :(得分:0)

您无法访问外部子查询,因为您必须重复代码

SELECT emp.employeeid
, emp.firstname
, CASE 
  WHEN (
    SELECT count(*) 
    FROM  (
    SELECT employeeid, shipperid 
    FROM Orders 
    WHERE orderdate > sysdate - 30
    ) o 
    WHERE o.shipperid <= 15
  ) > 10 THEN 'Almost'
WHEN (
  SELECT count(*) 
  FROM  (
    SELECT employeeid, shipperid 
    FROM Orders 
    WHERE orderdate > sysdate - 30
    ) o
    WHERE o.shipperid <= 15) > 20 THEN 'Yes'
ELSE 'No' END AS "Quota Met"
FROM Em  ployee emp
INNER JOIN  (
SELECT employeeid, shipperid 
FROM Orders 
WHERE orderdate > sysdate - 30

) o ON o.employeeid = emp.id
WHERE emp.zipcode = 22151;

如果不想重复代码,则可以使用视图

create my_view as  
SELECT employeeid, shipperid 
  FROM Orders 
  WHERE orderdate > sysdate - 30


SELECT emp.employeeid
, emp.firstname
, CASE 
  WHEN (
    SELECT count(*) 
    FROM my_view o
    WHERE o.shipperid <= 15
  ) > 10 THEN 'Almost'
WHEN (
  SELECT count(*) 
  FROM  my_view o
    WHERE o.shipperid <= 15) > 20 THEN 'Yes'
ELSE 'No' END AS "Quota Met"
FROM Em  ployee emp
INNER JOIN  my_viewq o ON o.employeeid = emp.id
WHERE emp.zipcode = 22151;