Oracle SQL 选择不同的查询以返回特定的行数
Posted
技术标签:
【中文标题】Oracle SQL 选择不同的查询以返回特定的行数【英文标题】:Oracle SQL select distinct query to return specific number of rows 【发布时间】:2011-08-10 08:32:37 【问题描述】:我在 Oracle 数据库中有一个表,其中包含一个时间戳列 nextTime 和一个字符串列 destName。还有更多列,但在这种情况下只有这两个是相关的。我正在尝试设计一个查询,该查询将返回具有特定间隔内的 nextTime 的不同 destName,并且返回的行数最多应为一千。当间隔内有超过一千个不同的 destName 时,我希望查询返回一千行,不多也不少。
我实际上有一个正在运行的查询,但它太慢了:
select destName
from (select /*+ index(tblDestNames tbldestnames_destname)*/ distinct destName
from (select /*+ index(tblDestNames tbldestnames_nextTime)*/ destName
from tblDestNames
where nextTime < :1 and nextTime >= :2 and destName is not null))
where rownum <= 1000;
非常感谢任何关于如何设计更智能的查询或如何优化现有查询的想法。
【问题讨论】:
参数 :1 和 :2 是否以 :1 > :2 的方式绑定?如果不是,查询将不会返回任何内容。如果是的话,最好写nextTime >= :2 and nextTime < :1
来强调参数的顺序。
@Aleksi:感谢您的评论!是的,:1 > :2 总是正确的。您建议的更改很有意义,我会应用它。
【参考方案1】:
我不确定是否有理由不将查询简化为:
select destName
from (
select distinct destName
from tblDestNames
where nextTime < :1 and nextTime >= :2 and destName is not null
)
where rownum <= 1000;
但是,这不会解决您的性能问题。问题是这样的:
where rownum <= 1000
通过将 rownum 替换为 'rank' 和 'over' 你会得到类似的结果:
select distinct destName
from (
select
destName
from
(select destName, rank()
over (order by destName desc ) rnk
from tblDestNames
where nextTime < :1 and nextTime >= :2 and destName is not null)
where rnk <= 1000;
)
好处是,您可以使用“结束”来选择显示结果和不显示结果的顺序。
编辑:实际上可以进一步简化为:
select
distinct destName
from
(select destName, rank()
over (order by destName desc ) rnk
from tblDestNames
where nextTime < :1 and nextTime >= :2 and destName is not null)
where rnk <= 1000;
【讨论】:
您不能将DISTINCT
移动到子查询中吗?之后,整个事情都可以在没有子查询的情况下完成。
Aleksi,我不确定,我试图将排序部分放在 oracle 抱怨 ORA-30483 的“where”子句中。我还尝试将其包含在“选择不同”部分中,但后来我无法将 rnk 限制为 1000 个结果。一定有解决方法,但我现在找不到。
rank
不能在WHERE
或HAVING
中使用,因此需要子查询。也许dense_rank
在这里会更合适,因为它不会跳过某些等级。现在,如果有 1000 个 destName x 实例,则下一个 destName 的排名为 1001,结果将仅包含一个 destName。
感谢您的回复!查询肯定可以按照第一个代码块中的建议进行简化。但是,最后一条语句似乎并没有提高性能。内部查询可能会返回相当多的行并对它们进行排序,然后选择不同的 destName 可能非常昂贵。你认为有办法减少内部查询返回的行数吗?可以跳过排序吗?
对我来说,解决方案是有 3 个 SELECT 级别:最深的一个具有“distinct”以仅获取唯一值,然后另一个选择将使用 rank() 包装它,因此每个唯一结果都有后续秩。然后最外层的选择将在排名上应用 WHERE 子句。如果我在外部 SELECT 中使用 DISTINCT,那么 rank() 仍将引用非唯一结果,因此限制将应用于比我实际的唯一结果高得多的排名。【参考方案2】:
我捡到的东西
-
您应该将执行计划优化留给 RDBMS,除非您真的了解得更清楚
无需从最里面的子查询返回重复名称
语义略有不同的更简单查询:
SELECT destName
FROM (SELECT DISTINCT destName
FROM tblDestNames
WHERE destName IS NOT NULL
AND nextTime NOT BETWEEN :1 and :2)
WHERE rownum <= 1000;
请注意,BETWEEN
包含在内,即 x BETWEEN y AND z
等于 y <= x <= z
。要排除上限,您要么必须按照您的方式进行操作,要么将参数 :2 减少一个单位 nextTime 。
【讨论】:
感谢您的回答!建议的查询绝对是原始查询的更好表示。但是,性能并没有太大的提高。可能有相当多的记录满足内部查询的要求(不为空和介于两者之间)。选择独特的成本会很高吗?我首先尝试不使用嵌套查询,但问题是如果前 1000 行包含相同的 destName,我只能在结果集中获得 1 行。以上是关于Oracle SQL 选择不同的查询以返回特定的行数的主要内容,如果未能解决你的问题,请参考以下文章
如何编写oracle SQL查询以特定顺序获取匹配和不匹配的行对(基于键列)
需要帮助修复 Oracle SQL 查询以返回具有最大列值的行
如何选择包含特定子字符串的单词列表作为 SQL 查询(oracle)的一部分?