select *
from 
(select
    exam_id as tid,
    count(distinct uid) as uv,
    count(*) as pv
from exam_record
group by exam_id
order by uv desc, pv desc)as t1
union all 
select * 
from
(select
    question_id as tid,
    count(distinct uid) as uv,
    count(*) as pv
from practice_record
group by question_id
order by uv desc, pv desc)as t2

关键在于 SQL 标准对 UNION ALL 的限制

UNIONUNION ALL 的各个分支(SELECT 子句)中,ORDER BY 是不允许的,除非它在一个子查询或派生表(derived table)中。

也就是说:

  • 在 UNION ALL 语句的每个分支中,ORDER BY 不能直接生效。
  • 只有整个 UNION ALL 的结果集(整个联合后的结果)才能有一个总的 ORDER BY。
  • 如果你想在每个部分单独排序,你必须把它放进一个 子查询(subquery)或派生表(derived table),让排序在该作用域内完成。

在这段语句中:

(select
    exam_id as tid,
    count(distinct uid) as uv,
    count(*) as pv
from exam_record
group by exam_id
order by uv desc, pv desc)
union all 
(select
    question_id as tid,
    count(distinct uid) as uv,
    count(*) as pv
from practice_record
group by question_id
order by uv desc, pv desc)

SQL 解析器会在解析 UNION ALL 时抛弃两个分支的 ORDER BY,因为它不知道如何在未确定最终联合结果之前就对各自排序。

因此:

  • 两个分支各自的 ORDER BY 被忽略;
  • 系统会直接把两个结果集合并(无顺序保证);
  • 最终输出的顺序是 未定义的(随机的)。

为什么加上 SELECT * FROM (...) AS t1 就能工作

当你写成:

SELECT * FROM (
  SELECT ... ORDER BY ...
) AS t1

你实际上是在告诉 SQL:

“我先在内部生成一个排序好的临时表(派生表 t1),然后再拿这个排序好的结果去做下一步操作。”

此时 ORDER BY 的作用域被限制在 子查询内部,SQL 会:

  1. 执行内部的 SELECT ... ORDER BY ...;
  2. 把排序后的结果放进临时表;
  3. 再执行 UNION ALL。

因此,每个子查询(t1 和 t2)内部都各自是 有序结果集