【问题标题】:How can a correlated subquery inside a SELECT's CASE WHEN access outer table alias?SELECT 的 CASE WHEN 中的相关子查询如何访问外部表别名?
【发布时间】:2019-03-03 02:35:42
【问题描述】:

我使用的是 Oracle 12。我设计了一个简单的示例来尝试传达我面临的问题,因为实际查询过于复杂,无法在此处展示。这个简单的示例包含与复杂查询相同的条件和逻辑。我希望找到一种方法,让位于主 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中的子查询可以访问连接的表别名?

【问题讨论】:

  • 更新您的问题添加适当的数据样本和预期结果

标签: sql oracle


【解决方案1】:

你并不真的需要子查询;如果您将o.shipperid &lt;= 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;

你可能仍然希望它是一个外部连接,但如果不把它改回一个内部连接。

【讨论】:

  • 你的建议看起来很有希望,给了我希望。我现在正在尝试应用它们...
  • 谢谢。我能够应用您的建议来重组查询以避免我看到的失败。然后我意识到重组查询以使 JOIN 工作并没有达到我加快查询速度的最终目标。我加入的那个派生表独立于它加入到主查询的字段;我忘记了必须在 JOIN 工作之前设置和定义 JOINed 表,所以我试图通过 JOIN 本身来限制它是没有意义的。您的想法帮助我看到了重组的选择,尽管这是我以前没有想到的。
【解决方案2】:

你不能以表的形式访问外部子查询,你必须重复代码

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;

【讨论】:

  • 问题是,当没有匹配的employeeid 限制时,这个SELECT 是巨大的,并且需要15 多分钟才能完成。如果我使用 WITH(如视图),则需要 15 分钟;如果我将 SELECT 复制到两个位置,则需要 30 分钟 - 我之前尝试过。我试图避免在没有employeeid匹配条件的情况下执行SELECT,这样它就不会选择它找到的数百万条记录。
猜你喜欢
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-17
相关资源
最近更新 更多