mysql: 如何在 LEFT JOIN 后保存 ORDER BY 而不重新排序?

mysql: how to save ORDER BY after LEFT JOIN without reorder?

我有两个 table:

1) profiles

+----+---------+
| id | name    |
+----+---------+
|  1 | WILLIAM |
|  2 | JOHN    |
|  3 | ROBERT  |
|  4 | MICHAEL |
|  5 | JAMES   |
|  6 | DAVID   |
|  7 | RICHARD |
|  8 | CHARLES |
|  9 | JOSEPH  |
| 10 | THOMAS  |
+----+---------+

2) request_for_friendship

+----+---------+-------+
| id | from_id | to_id |
+----+---------+-------+
|  1 |       1 |     2 |
|  2 |       1 |     3 |
|  3 |       1 |     8 |
|  5 |       4 |     1 |
|  6 |       9 |     1 |
+----+---------+-------+

我需要对所有 profiles 进行一些排序,然后将其与 request_for_friendship

合并

例如,对所有用户进行某种排序:

mysql>     SELECT *
    ->     FROM  profiles
    ->     ORDER BY name ASC;
+----+---------+
| id | name    |
+----+---------+
|  8 | CHARLES |
|  6 | DAVID   |
|  5 | JAMES   |
|  2 | JOHN    |
|  9 | JOSEPH  |
|  4 | MICHAEL |
|  7 | RICHARD |
|  3 | ROBERT  |
| 10 | THOMAS  |
|  1 | WILLIAM | <-- WILLIAM IS LAST!
+----+---------+

一切看起来都不错,正在排序。之后我加入 request_for_friendship 并且我的字符串会中断:

mysql> SELECT * FROM
    -> (
    ->     SELECT *
    ->     FROM  profiles
    ->     ORDER BY name ASC
    -> ) as users
    ->     LEFT JOIN request_for_friendship
    ->     AS request_for_friendship_copy
    ->     ON
    ->     (
    ->         request_for_friendship_copy.from_id = 1
    ->         AND
    ->         request_for_friendship_copy.to_id = users.id
    ->     )
    ->     OR
    ->     (
    ->         request_for_friendship_copy.from_id = users.id
    ->         AND
    ->         request_for_friendship_copy.to_id = 1
    ->     );
+----+---------+------+---------+-------+
| id | name    | id   | from_id | to_id |
+----+---------+------+---------+-------+
|  2 | JOHN    |    1 |       1 |     2 |
|  3 | ROBERT  |    2 |       1 |     3 |
|  8 | CHARLES |    3 |       1 |     8 |
|  4 | MICHAEL |    5 |       4 |     1 |
|  9 | JOSEPH  |    6 |       9 |     1 |
|  1 | WILLIAM | NULL |    NULL |  NULL | <-- WILLIAM IN THE MIDDLE!
|  5 | JAMES   | NULL |    NULL |  NULL |
|  6 | DAVID   | NULL |    NULL |  NULL |
|  7 | RICHARD | NULL |    NULL |  NULL |
| 10 | THOMAS  | NULL |    NULL |  NULL |
+----+---------+------+---------+-------+

如何JOIN LEFT使用原始排序保存?

我无法在 JOIN LEFT 之后对结果进行排序,因为当我在 JOIN 之前执行 ORDER BY 时,需要大约 0.02 秒 在我的数据库中(约 1 000 000 个用户)但是当我在 JOIN 之后执行 ORDER BY 时需要约 3.2 秒,这是非常长的时间:(

Demo: rextester.com/DLLM29415

Demo: http://sqlfiddle.com/#!9/167792/1

在sqlfiddle命令中保存!但是怎么办? MySQL5.6保存订单?

试试这个:

SELECT
    a.name as `from_name`,
    b.name as `to_name`,
    c.from_id,
    c.to_id
FROM profiles a
LEFT JOIN request_for_friendship c
ON a.id = c.from_id
LEFT JOIN profiles b
ON c.to_id = b.id
GROUP BY a.name,b.name
ORDER BY a.name,b.name;

或者,如果您希望每个 "from" 名称一行:

SELECT
    a.name as `from_name`,
    IFNULL(GROUP_CONCAT(b.name),'-none-') as `to_name`,
    IFNULL(c.from_id,'-none-') as `from_id`,
    IFNULL(GROUP_CONCAT(c.to_id),'-none-') as `to_id`
FROM profiles a
LEFT JOIN request_for_friendship c
ON a.id = c.from_id
LEFT JOIN profiles b
ON c.to_id = b.id
GROUP BY a.name
ORDER BY a.name,b.name
SELECT * 
  FROM profiles p 
  LEFT 
  JOIN request_for_friendship r 
    ON (r.from_id = p.id AND r.to_id = 1) 
    OR (r.from_id = 1 AND r.to_id = p.id)  
 ORDER 
    BY name;
+----+---------+------+---------+-------+
| id | name    | id   | from_id | to_id |
+----+---------+------+---------+-------+
|  8 | CHARLES |    3 |       1 |     8 |
|  6 | DAVID   | NULL |    NULL |  NULL |
|  5 | JAMES   | NULL |    NULL |  NULL |
|  2 | JOHN    |    1 |       1 |     2 |
|  9 | JOSEPH  |    6 |       9 |     1 |
|  4 | MICHAEL |    5 |       4 |     1 |
|  7 | RICHARD | NULL |    NULL |  NULL |
|  3 | ROBERT  |    2 |       1 |     3 |
| 10 | THOMAS  | NULL |    NULL |  NULL |
|  1 | WILLIAM | NULL |    NULL |  NULL |
+----+---------+------+---------+-------+
10 rows in set (0.02 sec)

mysql>

(解释 ORDER BY 的丢失)

SQL 标准本质上说子查询是一组无序的行。这意味着优化器可以随意忽略 'derived' table 中的 ORDER BYFROM ( SELECT ... ORDER BY )。在 MySQL 和 MariaDB 的 "recent" 版本中,这样的 ORDER BYs 被删除了。还有其他情况 ORDER BY 被忽略。

一些情况下(不确定这个),在ORDER BY之后添加一个LIMIT 99999999(大数字)欺骗优化器做ORDER BY。不过后面的"order"还是可以随意忽略的

MySQL 的一般规则:避免子查询。 (有些情况下子查询更快,但不是你的。)

重要规则:如果要对结果进行排序,必须在最外层设置 ORDER BY

如果您在第一个查询中将 LIMIT 3 添加到派生的 table,您将只会得到 CHARLES、DAVID、JAMES、,但不一定按此顺序。也就是说,您需要两个 ORDER BYs - 一个在派生 table 中,一个在最后。

我知道这个问题已经有几年了,但我没有发现已经提供了这个可能的解决方案。这是让子查询结果保持正确顺序的最佳解决方案。

考虑将 "row_number" 添加到您的子查询。然后在 row_number.

上使用 ORDER BY

这解释了如何添加 row_number: select increment counter in mysql

在我的例子中,我在分层递归查询中有未知数量的可能行​​,我需要保持子查询的顺序结果在外部查询中保持不变。

这是我的查询:

SELECT l.row_number, l.userid, l.child, p.id, p.username
FROM (

    SELECT  @rownum := @rownum + 1 AS row_number, u.parent AS userid, _id  AS child 
                FROM (
                    SELECT  @r AS _id, (SELECT  @r := parent FROM new_clean WHERE userid = _id) AS parent
                                FROM (SELECT @r := ?) AS vars, new_clean h
                                WHERE   @r <> 0 
                    ) u
                CROSS JOIN (SELECT @rownum := 0) r
                WHERE u.parent <> 0                     
    ) l 

        LEFT JOIN profile p ON p.userid = l.userid
        ORDER BY row_number