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: 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 BY
:FROM ( 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
我有两个 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: 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 BY
:FROM ( 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