Oracle SQL:根据每个分区中的第一行进一步对 PARTITION BY 组进行排序
Oracle SQL: Further sort PARTITION BY groups based on first row in each partition
我目前有一个非常大的查询,并且正在尝试以特定方式进一步对数据进行排序。按以下方式查询 returns 数据,项目按 TimeToComplete 降序排列:
| id | ItemKey |TimeToComplete|
| -------- | -------------- |--------------|
| ABC | KEY-211-01 | 580 |
| DEF | KEY-311-01 | 456 |
| GHI | KEY-111-01 | 150 |
| JKL | KEY-411-01 | 87 |
| XYZ | KEY-311-01 | 23 |
| KNE | KEY-211-01 | 17 |
我想做的是对这些数据进行排序,以便将 ItemKeys 分组在一起,但除此之外仍然保留基于组中第一项的 TimeToComplete 排序。像这样的例子:
| id | ItemKey |TimeToComplete|
| -------- | -------------- |--------------|
| ABC | KEY-211-01 | 580 |
| KNE | KEY-211-01 | 17 |
| DEF | KEY-311-01 | 456 |
| XYZ | KEY-311-01 | 23 |
| GHI | KEY-111-01 | 150 |
| JKL | KEY-411-01 | 87 |
我确实有一个部分工作的例子,但它有一些问题:
WITH GroupedRecords AS (
SELECT
OriginalQuery.*,
ROW_NUMBER() OVER (partition by ItemKey order by TimeToComplete DESC) as RN
FROM (originally giant query here) OriginalQuery
),
Sequence AS (
SELECT
ItemKey,
TimeToComplete,
ROW_NUMBER() OVER (order by TimeToComplete DESC) as SequenceOrder
FROM GroupedRecords
WHERE RN = 1
)
select T.*, s.SequenceOrder
from GroupedRecords T
INNER JOIN Sequence S ON T.ItemKey = S.ItemKey
ORDER BY S.SequenceOrder ASC, T.TimeToComplete DESC
问题是:
- GroupedRecords 和 Sequence 之间的内部连接将我的一堆列名(但不是全部)更改为 Oracle 随机生成的名称 (QCSJ_0000006)
- 连接也使查询速度太慢(OriginalQuery 已经不是很优化,但这使其执行时间加倍)
问题:
有没有更有效的方法可以在不使用 sequence/join 部分的情况下实现这种排序?
只需将以下 window 函数添加到您的 select 列表中,然后按如下顺序排列结果:
SELECT id, ItemKey, TimeToComplete
, MAX(TimeToComplete) OVER (PARTITION BY ItemKey) AS max_time
FROM data
ORDER BY max_time DESC, ItemKey, TimeToComplete DESC
;
结果:
+------+------------+----------------+----------+
| id | ItemKey | TimeToComplete | max_time |
+------+------------+----------------+----------+
| ABC | KEY-211-01 | 580 | 580 |
| KNE | KEY-211-01 | 17 | 580 |
| DEF | KEY-311-01 | 456 | 456 |
| XYZ | KEY-311-01 | 23 | 456 |
| GHI | KEY-111-01 | 150 | 150 |
| JKL | KEY-411-01 | 87 | 87 |
+------+------------+----------------+----------+
结果,当两个 ItemKey
具有相同的 max_time
:
时要测试的数据
+------+------------+----------------+----------+
| id | ItemKey | TimeToComplete | max_time |
+------+------------+----------------+----------+
| ABC | KEY-211-01 | 580 | 580 |
| KNE | KEY-211-01 | 17 | 580 |
| ABD | KEY-211-02 | 580 | 580 |
| ABE | KEY-211-02 | 200 | 580 |
| DEF | KEY-311-01 | 456 | 456 |
| XYZ | KEY-311-01 | 23 | 456 |
| GHI | KEY-111-01 | 150 | 150 |
| JKL | KEY-411-01 | 87 | 87 |
+------+------------+----------------+----------+
您可以在没有额外列的情况下生成相同的结果,方法是使用 CTE 术语添加新列,按外部查询表达式中的列排序,但不要 select 外部查询中的该列表达式 select 列表。
如下:
WITH cte1 AS (
SELECT id, ItemKey, TimeToComplete
, MAX(TimeToComplete) OVER (PARTITION BY ItemKey) AS max_time
FROM data
)
SELECT id, ItemKey, TimeToComplete
FROM cte1
ORDER BY max_time DESC, ItemKey, TimeToComplete DESC
;
结果(使用额外数据更新):
+------+------------+----------------+
| id | ItemKey | TimeToComplete |
+------+------------+----------------+
| ABC | KEY-211-01 | 580 |
| KNE | KEY-211-01 | 17 |
| ABD | KEY-211-02 | 580 |
| ABE | KEY-211-02 | 200 |
| DEF | KEY-311-01 | 456 |
| XYZ | KEY-311-01 | 23 |
| GHI | KEY-111-01 | 150 |
| JKL | KEY-411-01 | 87 |
+------+------------+----------------+
Working test case - Updated to handle case raised by @mathguy
幸运的是,您只需要在 order by
子句中添加一个解析 max()
。您不需要做 任何其他事情。
假设“当前查询”是您现有的查询,尚未以任何方式排序(无 order by
子句)。在最后添加以下内容:
... existing query ...
order by max(timetocomplete) over (partition by itemkey) desc,
itemkey,
timetocomplete desc
;
请注意,您 不需要 将分析函数添加到 select
子句。 SQL 标准说你可以; Oracle 语法告诉您不需要。 Oracle 在幕后为我们处理额外的小步骤。
这将计算完成每个键的最长时间。它首先按那个最大值排序。在平局的情况下(两个或多个不同的键具有相同的 max 完成时间),它进一步按 key 首先排序,然后在每个键,按时间完成(降序)。
我目前有一个非常大的查询,并且正在尝试以特定方式进一步对数据进行排序。按以下方式查询 returns 数据,项目按 TimeToComplete 降序排列:
| id | ItemKey |TimeToComplete|
| -------- | -------------- |--------------|
| ABC | KEY-211-01 | 580 |
| DEF | KEY-311-01 | 456 |
| GHI | KEY-111-01 | 150 |
| JKL | KEY-411-01 | 87 |
| XYZ | KEY-311-01 | 23 |
| KNE | KEY-211-01 | 17 |
我想做的是对这些数据进行排序,以便将 ItemKeys 分组在一起,但除此之外仍然保留基于组中第一项的 TimeToComplete 排序。像这样的例子:
| id | ItemKey |TimeToComplete|
| -------- | -------------- |--------------|
| ABC | KEY-211-01 | 580 |
| KNE | KEY-211-01 | 17 |
| DEF | KEY-311-01 | 456 |
| XYZ | KEY-311-01 | 23 |
| GHI | KEY-111-01 | 150 |
| JKL | KEY-411-01 | 87 |
我确实有一个部分工作的例子,但它有一些问题:
WITH GroupedRecords AS (
SELECT
OriginalQuery.*,
ROW_NUMBER() OVER (partition by ItemKey order by TimeToComplete DESC) as RN
FROM (originally giant query here) OriginalQuery
),
Sequence AS (
SELECT
ItemKey,
TimeToComplete,
ROW_NUMBER() OVER (order by TimeToComplete DESC) as SequenceOrder
FROM GroupedRecords
WHERE RN = 1
)
select T.*, s.SequenceOrder
from GroupedRecords T
INNER JOIN Sequence S ON T.ItemKey = S.ItemKey
ORDER BY S.SequenceOrder ASC, T.TimeToComplete DESC
问题是:
- GroupedRecords 和 Sequence 之间的内部连接将我的一堆列名(但不是全部)更改为 Oracle 随机生成的名称 (QCSJ_0000006)
- 连接也使查询速度太慢(OriginalQuery 已经不是很优化,但这使其执行时间加倍)
问题: 有没有更有效的方法可以在不使用 sequence/join 部分的情况下实现这种排序?
只需将以下 window 函数添加到您的 select 列表中,然后按如下顺序排列结果:
SELECT id, ItemKey, TimeToComplete
, MAX(TimeToComplete) OVER (PARTITION BY ItemKey) AS max_time
FROM data
ORDER BY max_time DESC, ItemKey, TimeToComplete DESC
;
结果:
+------+------------+----------------+----------+
| id | ItemKey | TimeToComplete | max_time |
+------+------------+----------------+----------+
| ABC | KEY-211-01 | 580 | 580 |
| KNE | KEY-211-01 | 17 | 580 |
| DEF | KEY-311-01 | 456 | 456 |
| XYZ | KEY-311-01 | 23 | 456 |
| GHI | KEY-111-01 | 150 | 150 |
| JKL | KEY-411-01 | 87 | 87 |
+------+------------+----------------+----------+
结果,当两个 ItemKey
具有相同的 max_time
:
+------+------------+----------------+----------+
| id | ItemKey | TimeToComplete | max_time |
+------+------------+----------------+----------+
| ABC | KEY-211-01 | 580 | 580 |
| KNE | KEY-211-01 | 17 | 580 |
| ABD | KEY-211-02 | 580 | 580 |
| ABE | KEY-211-02 | 200 | 580 |
| DEF | KEY-311-01 | 456 | 456 |
| XYZ | KEY-311-01 | 23 | 456 |
| GHI | KEY-111-01 | 150 | 150 |
| JKL | KEY-411-01 | 87 | 87 |
+------+------------+----------------+----------+
您可以在没有额外列的情况下生成相同的结果,方法是使用 CTE 术语添加新列,按外部查询表达式中的列排序,但不要 select 外部查询中的该列表达式 select 列表。
如下:
WITH cte1 AS (
SELECT id, ItemKey, TimeToComplete
, MAX(TimeToComplete) OVER (PARTITION BY ItemKey) AS max_time
FROM data
)
SELECT id, ItemKey, TimeToComplete
FROM cte1
ORDER BY max_time DESC, ItemKey, TimeToComplete DESC
;
结果(使用额外数据更新):
+------+------------+----------------+
| id | ItemKey | TimeToComplete |
+------+------------+----------------+
| ABC | KEY-211-01 | 580 |
| KNE | KEY-211-01 | 17 |
| ABD | KEY-211-02 | 580 |
| ABE | KEY-211-02 | 200 |
| DEF | KEY-311-01 | 456 |
| XYZ | KEY-311-01 | 23 |
| GHI | KEY-111-01 | 150 |
| JKL | KEY-411-01 | 87 |
+------+------------+----------------+
Working test case - Updated to handle case raised by @mathguy
幸运的是,您只需要在 order by
子句中添加一个解析 max()
。您不需要做 任何其他事情。
假设“当前查询”是您现有的查询,尚未以任何方式排序(无 order by
子句)。在最后添加以下内容:
... existing query ...
order by max(timetocomplete) over (partition by itemkey) desc,
itemkey,
timetocomplete desc
;
请注意,您 不需要 将分析函数添加到 select
子句。 SQL 标准说你可以; Oracle 语法告诉您不需要。 Oracle 在幕后为我们处理额外的小步骤。
这将计算完成每个键的最长时间。它首先按那个最大值排序。在平局的情况下(两个或多个不同的键具有相同的 max 完成时间),它进一步按 key 首先排序,然后在每个键,按时间完成(降序)。