2

I've two 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 |
+----+---------+-------+

I need to get all profiles with some sorting and join it with request_for_friendship

For example, get all users with some sorting:

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!
+----+---------+

Everything looks good, sorting is present. After that I join with request_for_friendship and my sotring will breaks:

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 |
+----+---------+------+---------+-------+

How to JOIN LEFT with original sorting saving?

I can't sort result after JOIN LEFT besause when I do ORDER BY before JOIN it takes ~0.02s in my db (~1 000 000 users) but when I do ORDER BY after JOIN it takes ~3.2s, it's very big time :(

Demo: rextester.com/DLLM29415

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

In sqlfiddle order is saved! But how? MySQL 5.6 saved order?

mixalbl4
  • 3,507
  • 1
  • 30
  • 44
  • 1
    you should do after join all table only – krishn Patel Apr 28 '17 at 10:54
  • @knowledge.... it totally destroy perfomance – mixalbl4 Apr 28 '17 at 10:55
  • 1
    It has to be at the end of the statement. All other Order By's are ignored. – Magnus Apr 28 '17 at 10:56
  • 1
    Perhaps you need to include Name in an index. – Magnus Apr 28 '17 at 11:01
  • 3
    If you put the `order by` in it's proper place (at the end), you get the correct result. If that causes unacceptable query execution time, that is a new question. You can't say "I can't do it the right way, because than the query takes too long". – HoneyBadger Apr 28 '17 at 11:02
  • 1
    if you remove it completly does the result changes? does it have an effect if it's there? – Charif DZ Apr 28 '17 at 11:04
  • @Magnus Very strange situation! In sqlfiddle order is saved! http://sqlfiddle.com/#!9/167792/1 But How? MySQL 5.6 save original order? – mixalbl4 Apr 28 '17 at 11:11
  • but why all this query what is wrong with just this: `SELECT * FROM profiles 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 = profiles.id ) OR ( request_for_friendship_copy.from_id = profiles.id AND request_for_friendship_copy.to_id = 1 ) order by profiles.name ` ?!! – Charif DZ Apr 28 '17 at 11:23
  • @CherifOdoo I've already ansered! -> I can't sort result after JOIN LEFT besause when I do ORDER BY before JOIN it takes ~0.02s in my db but when I do ORDER BY after JOIN it takes ~3.2s, it's very big time :( <- – mixalbl4 Apr 28 '17 at 11:27
  • it's taking 0.02 because it's not doing the order by at all i removed the order by and it gives the same result. – Charif DZ Apr 28 '17 at 11:29
  • @CherifOdoo Really? test db with 10 000 users: http://s1.micp.ru/cE1aB.png, with 1 000 000 takes ~4 sec with order and ~0.02 sec without order! – mixalbl4 Apr 28 '17 at 11:36
  • 1
    @MixerOID if you remove the order by from the subquery nothing will change the result it's like is beeing ignored and i'm not judging you i'm just trying to figure it out too – Charif DZ Apr 28 '17 at 11:40
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/142906/discussion-between-mixeroid-and-cherif-odoo). – mixalbl4 Apr 28 '17 at 11:45

4 Answers4

4

(Explaining the loss of ORDER BY)

The SQL standard essentially says that a subquery is an unordered set of rows. This implies that the Optimizer is free to ignore the ORDER BY in the 'derived' table: FROM ( SELECT ... ORDER BY ). In "recent" versions of MySQL and MariaDB, such ORDER BYs are being dropped. There are other cases where ORDER BY is ignored.

In some situations (not sure about this one), adding a LIMIT 99999999 (big number) after the ORDER BY tricks the Optimizer into doing the ORDER BY. However, it is still free to ignore the "order" later.

A general rule for MySQL: Avoid subqueries. (There are cases where subqueries are faster, but not yours.)

A strong rule: You must have an ORDER BY on the outermost if you want the results to be sorted.

If you had added LIMIT 3 to the derived table in your first query, you would get only CHARLES, DAVID, JAMES, but not necessarily in that order. That is, you would need two ORDER BYs - one in the derived table, one at the very end.

Rick James
  • 135,179
  • 13
  • 127
  • 222
  • Dude you are cool! The query began to work twice as fast! Your answer completely closes my question :) http://s2.micp.ru/VtWxY.png – mixalbl4 May 01 '17 at 07:47
0

Try this:

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;

Or, if you want one row per "from" name:

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
Sloan Thrasher
  • 4,953
  • 3
  • 22
  • 40
  • This is a completely different result – mixalbl4 Apr 28 '17 at 11:17
  • I've updated my answer, and added a 2nd version. The first version will return one row for each combination of from / to name, plus those without requests. The 2nd returns one row per user, with a comma separated list of requests. – Sloan Thrasher Apr 28 '17 at 17:57
  • In full version of my sql I've beg difficult ordering like this: `ORDER BY users.city_id <> ?, users.country_id <> ?, nearest_age, RAND()` with `SELECT users.*, ABS(users.age - ?) AS nearest_age`. Main question of my topic is: `How to JOIN LEFT with original sorting saving?` and `In sqlfiddle order is saved! But how? MySQL 5.6 saved order?` – mixalbl4 Apr 30 '17 at 07:41
  • So add your order by to the query. BTW, having booleans in the order by isn't the best way -- use the IF function and return 1 or 0 instead. – Sloan Thrasher Apr 30 '17 at 10:38
0
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>
Strawberry
  • 33,750
  • 13
  • 40
  • 57
  • 1
    This query is markedly different from the one you provided above. Further, questions about performance always require, as a minimum, CREATE table statements for all relevant tables AND the results of the EXPLAIN, and finally, I'm really not a fan of your tone, so I'm bowing out of this one. – Strawberry Apr 28 '17 at 11:48
  • Nope. I don't do pictures. – Strawberry Apr 28 '17 at 11:53
0

I know this question is a couple of years old, but I didn't find this possible solution already offered. This is the solution that worked best for me to keep the subquery results in the correct order.

Consider adding a "row_number" to your subquery. Then use ORDER BY on row_number.

This explains how to add the row_number: select increment counter in mysql

In my case, I have an unknown number of possible rows in a hierarchical recursive query that I need to keep the order results of the subquery to remain the same in the outer query.

This is my query:

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
RedSands
  • 145
  • 1
  • 14
  • Reading & writing the same variable in the same select statement is undefined behaviour in MySQL. – philipxy Aug 28 '19 at 23:20
  • @philipxy you may be right. And I am not an expert by any means. I have reviewed much on this topic here on stackoverflow for insight. I do think for those searching, they may also find this helpful: https://stackoverflow.com/questions/20215744/how-to-create-a-mysql-hierarchical-recursive-query/33737203#33737203 – RedSands Aug 30 '19 at 00:11