【问题标题】:postgresql selecting highest values [closed]postgresql选择最高值[关闭]
【发布时间】:2015-12-22 18:13:44
【问题描述】:

我有以下学生表:

Student_Name Student_RollNo Student_Marks1 Student_Marks2
Andrew       10             90.9           9.2
Blake        2              78.2           100
Fisher       1              69.2           89
Alex         1              98.8           82.1

我的查询:

select * 
from Student
order by (Student_Marks1+Student_Marks2) limit 1;

即我想检索分数最高的学生的学生详细信息。这里最高分是一个函数,它是 Student_Marks1 和 Student_Marks2 的总和。现在我很想知道 postgresql(或 mysql)使用哪种算法来执行这个排名?

即 postgresql 先应用 ORDER BY 再应用 LIMIT;还是先应用 LIMIT,然后再应用 ORDER BY;或者它是否使用将 ORDER BY 和 LIMIT 一起应用的算法

【问题讨论】:

  • 自然排序,从低到高。如果是数值类型的列,则根据数值进行排序。如果是文本列,则根据列所用字符集的排序规则进行排序(基本上按字母顺序)。如果您想要最大的,则按降序排列:order by Student_Marks desc
  • @DBug 是的,您说得非常正确,但是如果我想根据多个属性(或列)进行排序怎么办。然后我很想知道 postgresql 使用哪种排名算法来执行这个排名
  • 你在问 ORDER BY 是如何工作的?
  • @muistooshort 是的,我在问 ORDER BY 和 STOP AFTER 如何在 postgresql 中一起工作。我的意思是 postgresql 是先使用 ORDER BY 然后 STOP AFTER 还是反之亦然还是 postgresql 一起使用它们?
  • Postgres 必须首先应用order by,否则它无法确定“第一行”是什么。至于实际的排序,我认为 Postgres 使用了归并排序。使用explain (analyze, verbose)检查执行计划我想它会告诉你。

标签: sql database postgresql postgresql-9.1


【解决方案1】:

一般来说算法很简单:

  1. Student 表中检索行
  2. 使用 ORDER BY 表达式从 #1 开始对整个结果集进行排序
  3. 应用 LIMIT 子句(+ 偏移量)从 #2 获得的有序结果集中获取部分行

您可以在此处阅读有关 LIMIT 的更多信息:http://www.postgresql.org/docs/9.4/static/queries-limit.html

在某些情况下,在 ORDER BY 操作(排序)期间会考虑 LIMIT 以加快查询速度,尤其是当某些索引可用于消除排序操作时。

您可以查看解释计划来检查这项工作。

假设在这个表上创建了一个索引:

create index student_mark1 on student(student_marks1);

此查询给出以下解释计划:

select * From student
order by student_marks2 
limit 1;

Limit  (cost=5.06..5.06 rows=1 width=178) (actual time=0.088..0.089 rows=1 loops=1)
  Output: student_name, student_rollno, student_marks1, student_marks2
  ->  Sort  (cost=5.06..5.57 rows=204 width=178) (actual time=0.088..0.088 rows=1 loops=1)
        Output: student_name, student_rollno, student_marks1, student_marks2
        Sort Key: student.student_marks2
        Sort Method: top-N heapsort  Memory: 25kB
        ->  Seq Scan on public.student  (cost=0.00..4.04 rows=204 width=178) (actual time=0.007..0.021 rows=204 loops=1)
              Output: student_name, student_rollno, student_marks1, student_marks2

你需要从下往上阅读这个计划。

第一个操作是Seq scan - 这意味着从磁盘读取所有行(整个表 - 参见actual rows = 204)。

然后执行排序操作(ORDER BY)。最后一个操作是 LIMIT 1(在计划的顶部)

将上述计划与此查询进行比较:

select * From student
order by student_marks1 
limit 1;

Limit  (cost=0.14..0.24 rows=1 width=178) (actual time=0.010..0.010 rows=1 loops=1)
  Output: student_name, student_rollno, student_marks1, student_marks2
  ->  Index Scan using student_mark1 on public.student  (cost=0.14..19.20 rows=204 width=178) (actual time=0.009..0.009 rows=1 loops=1)
        Output: student_name, student_rollno, student_marks1, student_marks2

这里跳过了排序阶段,因为我们可以使用索引以所需的顺序检索行 (ORDER BY student_marks1 => INDEX ON Student( student_marks1 ))。

请注意最底部操作中的Actual rows = 1:`索引扫描'。

这意味着,PostgreSQL 不会扫描整个索引,而是只从索引中检索 1(第一)行,因为它知道,查询有 LIMIT 1 个子句。 (有时会说 PostgreSQL 将限制 1 子句“下推”到索引扫描操作并使用它来减少索引中扫描条目的数量。

更多关于使用索引来加速 ORDER BY 的信息你可以在这里找到:http://www.postgresql.org/docs/8.3/static/indexes-ordering.html

如果您的问题中的查询,ORDER BY 子句包含表达式 Student_Marks1+Student_Marks2,而不是简单的列。此查询的解释计划如下所示:

select *
From student
order by student_marks1 + student_marks2 
limit 2;

Limit  (cost=7.10..7.11 rows=2 width=178) (actual time=0.207..0.207 rows=2 loops=1)
  Output: student_name, student_rollno, student_marks1, student_marks2, (((student_marks1)::numeric + student_marks2))
  ->  Sort  (cost=7.10..7.61 rows=204 width=178) (actual time=0.205..0.205 rows=2 loops=1)
        Output: student_name, student_rollno, student_marks1, student_marks2, (((student_marks1)::numeric + student_marks2))
        Sort Key: (((student.student_marks1)::numeric + student.student_marks2))
        Sort Method: top-N heapsort  Memory: 25kB
        ->  Seq Scan on public.student  (cost=0.00..5.06 rows=204 width=178) (actual time=0.019..0.107 rows=204 loops=1)
              Output: student_name, student_rollno, student_marks1, student_marks2, ((student_marks1)::numeric + student_marks2)

但是您仍然可以通过这种方式加快创建function based index 的查询:

create index student_mark12 on student( ( student_marks1 + student_marks2) );

创建索引后,我们现在有:

Limit  (cost=0.14..0.34 rows=2 width=178) (actual time=0.044..0.047 rows=2 loops=1)
  Output: student_name, student_rollno, student_marks1, student_marks2, (((student_marks1)::numeric + student_marks2))
  ->  Index Scan using student_mark12 on public.student  (cost=0.14..20.22 rows=204 width=178) (actual time=0.043..0.046 rows=2 loops=1)
        Output: student_name, student_rollno, student_marks1, student_marks2, ((student_marks1)::numeric + student_marks2)

请注意,Postgre 在这种情况下使用索引,并且根据LIMIT 2 子句仅从中检索 2 个条目(实际行 = 2)。

【讨论】:

    【解决方案2】:

    PostgreSQL 使用默认的 B-tree 运算符类来确定 ORDER BY 表达式中的排序顺序(ASC 作为默认值,使用 排序较大)。

    至于您对多个属性的评论问题,它会根据您输入的排序字段顺序使用 ORDER BY。

    【讨论】:

    • 我害怕......但是它如何在 2 个属性(即标记 1 和标记 2)上按顺序使用 ORDER BY。因为它需要对标记1和标记2的总和进行排序,然后使用限制,即检索最高记录
    • 另外,您能否请我参考一些文档,我可以从中阅读更多相关信息。
    猜你喜欢
    • 1970-01-01
    • 2022-01-21
    • 2015-08-27
    • 1970-01-01
    • 1970-01-01
    • 2020-08-19
    • 1970-01-01
    • 2021-12-07
    • 2016-09-29
    相关资源
    最近更新 更多