Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reverse join in MySQL

SO,

The problem

My question is about - how to join table in MySQL with itself in reverse order? Suppose I have:

id  name
1   First
2   Second
5   Third
6   Fourth
7   Fifth
8   Sixth
9   Seventh
13  Eight
14  Nine
15  Tenth

-and now I want to create a query, which will return joined records in reverse order:

left_id name    right_id    name 
   1    First      15   Tenth 
   2    Second     14   Nine 
   5    Third      13   Eight 
   6    Fourth      9   Seventh 
   7    Fifth       8   Sixth 
   8    Sixth       7   Fifth 
   9    Seventh     6   Fourth 
  13    Eight       5   Third 
  14    Nine        2   Second 
  15    Tenth       1   First 

My approach

I have now this query:

SELECT 
  l.id AS left_id, 
  l.name, 
  (SELECT COUNT(1) FROM sequences WHERE id<=left_id) AS left_order, 
  r.id AS right_id,
  r.name,
  (SELECT COUNT(1) FROM sequences WHERE id<=right_id) AS right_order 
FROM 
  sequences AS l 
  LEFT JOIN 
    sequences AS r ON 1
HAVING
  left_order+right_order=(1+(SELECT COUNT(1) FROM sequences));

-see this fiddle for sample structure & code.

Some background

There's no use case for that. I was doing that in application before. Now it's mostly curiosity if there's a way to do that in SQL - that's why I'm seeking not just 'any solution' (like mine) - but as simple as possible solution. Source table will always be small (<10.000 records) - so performance is not a thing to care, I think.

The question

Can my query be simplified somehow? Also, it's important not to use variables. Order could be included in result (like in my fiddle) - but that's not mandatory.

like image 540
Alma Do Avatar asked Oct 01 '13 10:10

Alma Do


People also ask

What is the reverse of inner join?

Answers. You can use Except and Not Exists and LEFT OUTER JOIN.

What is inverted left join?

a LEFT OUTER JOIN basically returns all rows from table A, with or without matching rows from table B. when there is no matching row from B, then the columns from B are all set to NULL in the result row. those are the rows we want to keep, and that's what the WHERE clause does.

What is the opposite of inner join in SQL?

If you consider an inner join as the rows of two tables that meet a certain condition, then the opposite would be the rows in either table that don't.

What is right join in MySQL?

The RIGHT JOIN keyword returns all records from the right table (table2), and the matching records from the left table (table1). The result is 0 records from the left side, if there is no match.


2 Answers

Without the SQL standard RANK() OVER(...), you have to compute the ordering yourself as you discovered.

The RANK() of a row is simply 1 + the COUNT() of all better-ranked rows. (DENSE_RANK(), for comparison, is 1 + the COUNT() of all DISTINCT better ranks.) While RANK() can be computed as a scalar subquery in your SELECT projection — as, e.g., you have done with SELECT (SELECT COUNT(1) ...), ... — I tend to prefer joins:

    SELECT lft.id AS "left_id", lft.name AS "left_name",
           rgt.id AS "right_id", rgt.name AS "right_name"
      FROM (   SELECT s.id, s.name, COUNT(1) AS "rank"     -- Left ranking
                 FROM sequences s
            LEFT JOIN sequences d ON s.id <= d.id
             GROUP BY 1, 2) lft
INNER JOIN (   SELECT s.id, s.name, COUNT(1) AS "rank"     -- Right ranking
                 FROM sequences s
            LEFT JOIN sequences d ON s.id >= d.id
             GROUP BY 1, 2) rgt
           ON lft.rank = rgt.rank
  ORDER BY lft.id ASC;
like image 30
pilcrow Avatar answered Sep 30 '22 18:09

pilcrow


The only thing i can think to be improved is

SELECT 
  l.id AS left_id, 
  l.name ln, 
  (SELECT COUNT(1) FROM sequences WHERE id<=left_id) AS left_order, 
  r.id AS right_id,
  r.name rn,
  (SELECT COUNT(1) FROM sequences WHERE id>=right_id) AS right_order 
FROM 
  sequences AS l 
  LEFT JOIN 
    sequences AS r ON 1
HAVING
  left_order=right_order;

There are 2 changes that should make this a little bit faster:

1) Calculating right order in reverse order in the first place

2) avoid using SELECT COUNT in the last line.

Edit: I aliased the ln,rn because i couldn't see the columns in fiddle

like image 50
Thanos Darkadakis Avatar answered Sep 30 '22 18:09

Thanos Darkadakis