有没有更有效的方法将多个 select 语句的第一行聚合成一个结果?
Is there a more efficient way to aggregate the top row from multiple select statements into a single result?
好吧,我在各种数据库方面都有经验,但对 Oracle 不是特别了解,所以我仍在努力思考它如何处理事情(即缺少 'Top' 让我发疯!)无论如何,这就是我们正在努力做...
注意:我们使用的是 Oracle 11。我知道 Oracle 12 中添加了一项新功能(即 FETCH NEXT n ROWS ONLY
),但不幸的是我们不能使用它。
首先,我们有一篇文章 table(从技术上讲,它是一个视图,但我在这里对其进行了简化)。以下是相关字段...
- ID(整数,主键)
- 标题(字符串)
- Body(字符串)
- 最喜欢的('Y' 或 'N')
- IsFeatured('Y' 或 'N')
- 发布日期(日期)
多篇文章可以标记为收藏夹and/or精选。
我们想要 return 的结果集具有以下顺序...
- most-recently 发表的文章
IsFeatured = 'Y'
,如果有的话。
- most-recently 发表的文章
IsFavorite = 'Y'
不是来自 #1 的行(如果最近的精选也是最近的收藏,这可以避免重复,而是选择 下一个最喜欢的行)如果有的话
- 三篇most-recently发表的非#1或#2的文章,如果有的话
这是我到目前为止的想法(cut/pasted/edited 这里可能有一些拼写错误),但这对我来说 'clunky' 感觉太...就像我在浪费许多不必要的处理和迭代。
WITH mostRecentlyFeatured as (
Select * from (
Select 2 as MAJOR_SORT, A.*
From Articles A
where IsFeatured = 'Y'
order by DatePublished DESC
)
where ROWNUM = 1
),
mostRecentlyFavorited as (
Select * from (
Select 1 as MAJOR_SORT, A.*
From Articles A
minus Select * From mostRecentlyFeatured
where IsFavorite = 'Y'
order by DatePublished DESC
)
where ROWNUM = 1
),
topThreeOthers as (
Select * from (
select 0 as MAJOR_SORT, A.*
from Articles
minus
SELECT * from mostRecentlyFeatured
minus
SELECT * from mostRecentlyFavorited
order by DatePublished desc
)
where ROWNUM <= 3
),
finalRows as (
Select * from mostRecentlyFeatured
union all
Select * from mostRecentlyFavorited
union all
select * from topThreeOthers
)
Select * from finalRows
Order By MAJOR_SORT DESC,
DatePublished DESC;
这不是最不常见的查询,所以我无法想象没有更好的方法来执行此操作,但我还没有看到它。那么有吗?
the lack of 'Top' drives me mad
由于您使用的是 Oracle 11g
,因此不支持 TOP-n。所以 ROWNUM
是唯一的出路。参见 。
例如,偏移量为 4 并使用 ROWNUM 获取下一个 4:
SQL> SELECT val
FROM (SELECT val, rownum AS rnum
FROM (SELECT val
FROM order_test
ORDER BY val)
WHERE rownum <= 8)
WHERE rnum >= 5;
VAL
----------
3
3
4
4
从 Oracle 12c
开始,您可以使用 Top-n row limiting 功能。由于您没有提供任何示例数据,这里有一个简单的演示:
SQL> select * from order_test order by val;
VAL
----------
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
20 rows selected.
前 4 行:
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 FETCH FIRST 4 ROWS ONLY;
VAL
----------
1
1
2
2
下 4 行(查看 OFFSET) :
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;
VAL
----------
3
3
4
4
最后,接下来的 4 行 OFFSET 8 行:
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 OFFSET 8 ROWS FETCH NEXT 4 ROWS ONLY;
VAL
----------
5
5
6
6
使用上面的方法,你可以得到标记为'IsFeatured'的最新发表的文章如下:
SELECT * FROM articles
WHERE isfeatured = true
ORDER BY datepublished DESC
FETCH FIRST ONE ROW ONLY;
这是您的查询的可能解决方案。它使用一组 CTE 来 select 第 n
篇精选文章、第 m
篇最喜欢的文章(不在第 n
篇精选文章中)以及总共k
篇文章:
- 第一个生成特色文章和收藏文章的行号;
- 第二个计算前
n
篇精选文章中有多少篇也是前 m
篇收藏;和
- 第三个为根据第一个
n
个精选文章、第一个 m
个收藏夹排序的文章生成行号(除非它们也属于第一个 n
个精选文章,在这种情况下选择下一个收藏夹)和发布日期。
最后 k
行是 select 从最后一个 CTE 编辑的。
这是一个查询,其中 n = 2
、m = 3
和 k = 8
:
-- first 2 (n) featured, first 3 (m) favourites, then 3 others (8 (k) total)
WITH favfeatrank AS (
-- rank articles by date and whether they are favourite or featured
SELECT ID, Title, IsFavorite, IsFeatured, DatePublished,
ROW_NUMBER() OVER (ORDER BY IsFeatured DESC, DatePublished DESC) feat,
ROW_NUMBER() OVER (ORDER BY IsFavorite DESC, DatePublished DESC) fav
FROM Articles
),
numfavfeat AS (
-- number of favourites that are also in the first n featured
-- use m in the next line
SELECT COUNT(CASE WHEN fav <= 3 THEN 1 END) AS featfav
FROM favfeatrank
WHERE feat <= 2 -- use n here
),
articlerank AS (
-- articles ranked according to featured, favourite and date published
SELECT ID, Title, IsFavorite, IsFeatured, DatePublished,
ROW_NUMBER() OVER (ORDER BY CASE WHEN feat <= 2 THEN 0 -- use n here
WHEN fav <= 3 + featfav THEN 1 -- use m here
ELSE 2
END,
DatePublished DESC) AS rn
FROM favfeatrank
CROSS JOIN numfavfeat
)
SELECT ID, Title, IsFavorite, IsFeatured, DatePublished
FROM articlerank
WHERE rn <= 8 -- use k here
好吧,我在各种数据库方面都有经验,但对 Oracle 不是特别了解,所以我仍在努力思考它如何处理事情(即缺少 'Top' 让我发疯!)无论如何,这就是我们正在努力做...
注意:我们使用的是 Oracle 11。我知道 Oracle 12 中添加了一项新功能(即 FETCH NEXT n ROWS ONLY
),但不幸的是我们不能使用它。
首先,我们有一篇文章 table(从技术上讲,它是一个视图,但我在这里对其进行了简化)。以下是相关字段...
- ID(整数,主键)
- 标题(字符串)
- Body(字符串)
- 最喜欢的('Y' 或 'N')
- IsFeatured('Y' 或 'N')
- 发布日期(日期)
多篇文章可以标记为收藏夹and/or精选。
我们想要 return 的结果集具有以下顺序...
- most-recently 发表的文章
IsFeatured = 'Y'
,如果有的话。 - most-recently 发表的文章
IsFavorite = 'Y'
不是来自 #1 的行(如果最近的精选也是最近的收藏,这可以避免重复,而是选择 下一个最喜欢的行)如果有的话 - 三篇most-recently发表的非#1或#2的文章,如果有的话
这是我到目前为止的想法(cut/pasted/edited 这里可能有一些拼写错误),但这对我来说 'clunky' 感觉太...就像我在浪费许多不必要的处理和迭代。
WITH mostRecentlyFeatured as (
Select * from (
Select 2 as MAJOR_SORT, A.*
From Articles A
where IsFeatured = 'Y'
order by DatePublished DESC
)
where ROWNUM = 1
),
mostRecentlyFavorited as (
Select * from (
Select 1 as MAJOR_SORT, A.*
From Articles A
minus Select * From mostRecentlyFeatured
where IsFavorite = 'Y'
order by DatePublished DESC
)
where ROWNUM = 1
),
topThreeOthers as (
Select * from (
select 0 as MAJOR_SORT, A.*
from Articles
minus
SELECT * from mostRecentlyFeatured
minus
SELECT * from mostRecentlyFavorited
order by DatePublished desc
)
where ROWNUM <= 3
),
finalRows as (
Select * from mostRecentlyFeatured
union all
Select * from mostRecentlyFavorited
union all
select * from topThreeOthers
)
Select * from finalRows
Order By MAJOR_SORT DESC,
DatePublished DESC;
这不是最不常见的查询,所以我无法想象没有更好的方法来执行此操作,但我还没有看到它。那么有吗?
the lack of 'Top' drives me mad
由于您使用的是 Oracle 11g
,因此不支持 TOP-n。所以 ROWNUM
是唯一的出路。参见
例如,偏移量为 4 并使用 ROWNUM 获取下一个 4:
SQL> SELECT val
FROM (SELECT val, rownum AS rnum
FROM (SELECT val
FROM order_test
ORDER BY val)
WHERE rownum <= 8)
WHERE rnum >= 5;
VAL
----------
3
3
4
4
从 Oracle 12c
开始,您可以使用 Top-n row limiting 功能。由于您没有提供任何示例数据,这里有一个简单的演示:
SQL> select * from order_test order by val;
VAL
----------
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
20 rows selected.
前 4 行:
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 FETCH FIRST 4 ROWS ONLY;
VAL
----------
1
1
2
2
下 4 行(查看 OFFSET) :
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;
VAL
----------
3
3
4
4
最后,接下来的 4 行 OFFSET 8 行:
SQL> SELECT val
2 FROM order_test
3 ORDER BY VAL
4 OFFSET 8 ROWS FETCH NEXT 4 ROWS ONLY;
VAL
----------
5
5
6
6
使用上面的方法,你可以得到标记为'IsFeatured'的最新发表的文章如下:
SELECT * FROM articles
WHERE isfeatured = true
ORDER BY datepublished DESC
FETCH FIRST ONE ROW ONLY;
这是您的查询的可能解决方案。它使用一组 CTE 来 select 第 n
篇精选文章、第 m
篇最喜欢的文章(不在第 n
篇精选文章中)以及总共k
篇文章:
- 第一个生成特色文章和收藏文章的行号;
- 第二个计算前
n
篇精选文章中有多少篇也是前m
篇收藏;和 - 第三个为根据第一个
n
个精选文章、第一个m
个收藏夹排序的文章生成行号(除非它们也属于第一个n
个精选文章,在这种情况下选择下一个收藏夹)和发布日期。
最后 k
行是 select 从最后一个 CTE 编辑的。
这是一个查询,其中 n = 2
、m = 3
和 k = 8
:
-- first 2 (n) featured, first 3 (m) favourites, then 3 others (8 (k) total)
WITH favfeatrank AS (
-- rank articles by date and whether they are favourite or featured
SELECT ID, Title, IsFavorite, IsFeatured, DatePublished,
ROW_NUMBER() OVER (ORDER BY IsFeatured DESC, DatePublished DESC) feat,
ROW_NUMBER() OVER (ORDER BY IsFavorite DESC, DatePublished DESC) fav
FROM Articles
),
numfavfeat AS (
-- number of favourites that are also in the first n featured
-- use m in the next line
SELECT COUNT(CASE WHEN fav <= 3 THEN 1 END) AS featfav
FROM favfeatrank
WHERE feat <= 2 -- use n here
),
articlerank AS (
-- articles ranked according to featured, favourite and date published
SELECT ID, Title, IsFavorite, IsFeatured, DatePublished,
ROW_NUMBER() OVER (ORDER BY CASE WHEN feat <= 2 THEN 0 -- use n here
WHEN fav <= 3 + featfav THEN 1 -- use m here
ELSE 2
END,
DatePublished DESC) AS rn
FROM favfeatrank
CROSS JOIN numfavfeat
)
SELECT ID, Title, IsFavorite, IsFeatured, DatePublished
FROM articlerank
WHERE rn <= 8 -- use k here