Oracle SQL 中结合 LIMIT 子句访问主表字段的子查询

Posted

技术标签:

【中文标题】Oracle SQL 中结合 LIMIT 子句访问主表字段的子查询【英文标题】:Subquery that accesses main table fields combined with LIMIT clause in Oracle SQL 【发布时间】:2016-12-22 11:36:04 【问题描述】:

我有一个表格用户和一个表格任务。任务按重要性排序并分配给用户的任务列表。任务有一个状态:就绪或未就绪。现在,我想列出所有用户以及他们最重要的任务也已准备就绪。

有趣的需求是每个用户的任务首先需要过滤和排序,然后应该选择最重要的一个。这是我想出的:

SELECT Users.name,
    (SELECT *
        FROM (SELECT Tasks.description
              FROM Tasks
              WHERE Tasks.taskListCode = Users.taskListCode AND Tasks.isReady
              ORDER BY Tasks.importance DESC)
        WHERE rownum = 1
    ) AS nextTask
    FROM Users

但是,这会导致错误

ORA-00904: "Users"."taskListCode": invalid identifier

我认为原因是oracle does not support correlating subqueries with more than one level of depth。但是,我需要两个级别才能执行WHERE rownum = 1

我也试过没有相关子查询:

SELECT Users.name, Task.description
FROM Users
LEFT JOIN Tasks nextTask ON
    nextTask.taskListCode = Users.taskListCode AND
    nextTask.importance = MAX(
        SELECT tasks.importance 
        FROM tasks
        WHERE tasks.isReady
        GROUP BY tasks.id
    )

这会导致错误

ORA-00934: group function is not allowed here

我将如何解决这个问题?

【问题讨论】:

WHERE task.isready 是什么意思? Oracle 数据库中没有布尔值。 【参考方案1】:

一种解决方法是使用keep

SELECT u.name,
       (SELECT MAX(t.description) KEEP (DENSE_RANK FIRST ORDER BY T.importance DESC)
        FROM Tasks t
        WHERE t.taskListCode = u.taskListCode AND t.isReady
       ) as nextTask
FROM Users u;

【讨论】:

这个MAX(t.description) 看起来很奇怪,或者它是解决方法所必需的?否则,这看起来比 Kacper 的答案更简洁。你知道性能是否不同吗? @Yogu 。 . .事实上,它看起来确实很奇怪。 MAX() 什么都不做,因为 DENSE_RANK FIRST 只匹配一个值。 . .so MIN()MAX() 会做同样的事情。我从来没有在可能返回多行值的上下文中使用KEEP;但我认为聚合函数可能在某些情况下有用。 @GordonLinoff - 如果按“重要性”排序不严格,“dense_rank first”可能会打成平手。可能存在三个具有相同、最高重要性的任务。然后 MAX 将按字母顺序选择具有最新描述的任务。 MIN 将按字母顺序选择第一个描述 - 从最重要的任务中。【参考方案2】:

请尝试解析函数:

with tp as (select t.*, row_number() over (partition by taskListCode order by importance desc) r 
            from tasks t 
            where isReady = 1 /*or 'Y' or what is positive value here*/)
select u.name, tp.description 
  from users u left outer join tp on (u.taskListCode = tp.taskListCode) 
  where tp.r = 1;

【讨论】:

【参考方案3】:

这是一个使用聚合而不是分析函数的解决方案。您可能希望针对分析函数解决方案运行它以查看哪个更快;在许多情况下,聚合查询(稍微)快一些,但这取决于您的数据、索引使用情况等。

这个解决方案与 Gordon 尝试做的类似。我不知道他为什么使用相关子查询而不是直接连接来编写它(并且不知道它是否会工作 - 我从未见过像这样的相关子查询使用 FIRST/LAST 函数)。

如果 importance 列中可能存在 NULL,则可能无法完全正确运行 - 那么您需要在 t.importance 之后和 ) 之前添加 nulls first。注意:max(t.description) 是必需的,因为可能存在“重要性”(对于给定用户具有相同、最高重要性的两个任务)的联系。在这种情况下,必须选择一项任务。如果按重要性排序是严格的(没有关系),那么MAX() 什么都不做,因为它在一组恰好一个值上选择 MAX,但编译器事先并不知道,所以它确实需要MAX()

select u.name, 
       max(t.description) keep (dense_rank last order by t.importance) as descr
from   users u left outer join tasks t on u.tasklistcode = t.tasklistcode
where  t.isready = 'Y'
group by u.name

【讨论】:

以上是关于Oracle SQL 中结合 LIMIT 子句访问主表字段的子查询的主要内容,如果未能解决你的问题,请参考以下文章

SQL:在超过 1500 万行查询中结合 WHERE、ORDER 和 LIMIT

解决方案 兼容 Oracle 和 MySQL for LIMIT 子句

Oracle 11g 中的限制子句

SQL 查询的 SELECT 子句中 Oracle PL/SQL 语句的延迟评估

等效于 SQL SERVER 的 MySQL LIMIT 子句

实习踩坑之路:一个诡异的SQL?PageHelper莫名多了一个Limit子句,导致SQL执行错误?