可以有一个与数据库无关的 SQL 查询来获取前 N 行吗?
Posted
技术标签:
【中文标题】可以有一个与数据库无关的 SQL 查询来获取前 N 行吗?【英文标题】:Can there be a database-agnostic SQL query to fetch top N rows? 【发布时间】:2010-10-23 22:05:56 【问题描述】:我们希望能够使用 SQL 查询选择前 N 行。目标数据库可以是 Oracle 或 mysql。有没有一种优雅的方法? (不用说,我们在这里处理已排序的数据。)
【问题讨论】:
【参考方案1】:要从该表中获得前 5 名得分手:
CREATE TABLE people
(id int,
name string,
score int)
试试这个 SQL:
SELECT id,
name,
score
FROM people p
WHERE (SELECT COUNT(*)
FROM people p2
WHERE p2.score > p.score
) <=4
我相信这应该适用于大多数地方。
【讨论】:
【参考方案2】:没有。语法不同。
但是,您可以创建视图:
/* Oracle */
CREATE VIEW v_table
AS
SELECT *
FROM (
SELECT *
FROM table
ORDER BY
column
)
WHERE rownum <= n
/* MySQL */
CREATE VIEW v_table
AS
SELECT *
FROM table
ORDER BY
column
LIMIT n
【讨论】:
受限于强制为您的视图排序(至少在 SQL 服务器上使用 TOP n 子句时)。 其他 DBMS 支持该概念但使用其他符号。 另外,一些 DBMS 不允许在视图中使用 ORDER BY 子句。 感谢您的回答。但是这个问题的主要动机是避免编写单独的查询。查看@kishore 建议的链接以及这些答案,似乎没有这样的方法。 您编写单独的查询一次,只是为了创建一个视图。要查询这些值,您只需发出 SELECT * FROM v_table 这两个数据库都相同。【参考方案3】:我认为即使只是在 mysql 和 mssql 之间也是不可能的。我做了一个模拟这种行为的选项:
-
创建具有自动递增 int 列的视图;说“PagingHelperID”
编写查询如:
SELECT columns FROM viewname WHERE PagingHelperID BETWEEN startindex AND stopindex
这将使订购变得困难,您需要为每个要检索数据的订单提供不同的视图。
您也可以在查询时根据数据库动态“重写”您的 sql,并为重写器定义您自己的方法,但我认为没有任何“好的”方法可以做到这一点。
【讨论】:
对。我想避免任何类型的数据库检测。【参考方案4】:如果表上有唯一键是...
Select * From Table O
Where (Select Count(*) From Table I
Where [UniqueKeyValue] < O.UniqueKeyValue) < N
如果您希望“Top”定义基于其他逻辑而不是唯一键...
编辑:如果定义“Top”含义的“排序”基于非唯一列或列集,那么您仍然可以使用它,但您不能保证您将能够准确地取出 N 条记录...
Select * From Table O
Where (Select Count(*) From Table I
Where nonUniqueCol < O.nonUniqueCol) < 10
如果记录 8、9、10、11 和 12 在 [nonUniqueCol] 中都具有相同的值,那么查询将要么只生成 7 条记录,(带有 '
注意:由于这涉及关联的子查询,因此对于非常大的表,性能可能是一个问题...
【讨论】:
如果您的唯一键是 GUID 或其他一些非顺序数据怎么办? 唯一键只允许您识别记录...如果定义“顶部”含义的“排序”是基于其他一些逻辑,那么子查询将被写入“计数”基于该逻辑的记录...唯一的问题是,如果 N 值出现在有多个实例的特定值上......(如果记录 8、9、10 和 11 都具有相同的值,您无法获得前 10 名)【参考方案5】:从 MySQL 8 开始,您可以使用 ROW_NUMBER()
过滤以统一、符合标准的语法获取 LIMIT
(MySQL) 或 FETCH
(Oracle) 的语义:
SELECT t.a, t.b, t.c, t.o
FROM (
SELECT a, b, c, o, ROW_NUMBER() OVER (ORDER BY o)
FROM x
) t
WHERE rn <= :limit
ORDER BY o
但这可能比使用供应商特定的语法效率低,所以如果你有一些抽象LIMIT
和FETCH
的方法(例如使用像jOOQ 或Hibernate 这样的ORM,甚至一些模板语言),应该是首选。
【讨论】:
【参考方案6】:仔细研究后,最大的问题是 MySQL 不符合 ISO SQL:2003 标准。如果是这样,您将拥有这些方便的窗口函数:
SELECT * from
( SELECT
RANK() OVER (ORDER BY <blah>) AS ranking,
<rest of columns here>,
FROM <table>
)
WHERE ranking <= <N>
唉,MySQL(和其他模仿它的行为,例如 SQLite)不这样做,因此整个限制问题。
查看来自***的这个 sn-p (http://en.wikipedia.org/wiki/Window_function_(SQL)#Limiting_result_rows)
【讨论】:
以上是关于可以有一个与数据库无关的 SQL 查询来获取前 N 行吗?的主要内容,如果未能解决你的问题,请参考以下文章