Home > Blockchain >  How to reference previous records in a query
How to reference previous records in a query

Time:03-11

For simplicity, let's say I have a table like this.

| id    |  a     |   b  |  c   |
| 1     | 100    |  b1  | c1   |
| 2     | 104    |  b2  | c2   |
| 3     | 106    |  b1  | c1   |
| 5     | 118    |  b2  | c2   |

I want to find next record which field b and c share the same value as previous one, and field a is greater than previous one. There may be more than one records match that, but I only want to get the first one which matches it. I do not know how to write such a SQL to get result like below. i.e.:

| id    |  a     |   b    |  c   | matched_id. | b(1) | c(1) |
| 1     | 100    |  b1    | c1   |   3         | b1   |  c1  |
| 2     | 104    |  b2    | c2   |   5         | b2   |  c2  |
| 3     | 106    |  b1    | c1   |   null      | null |  null|
| 5     | 118    |  b2    | c2   |   null      | null |  null|

BTW, I am using MySQL 5.7.

CodePudding user response:

This solution uses a self join and ROW_NUMBER to get only the first, whuich will work only in MySQL 8.

To show that it works i added another row with id 6

CREATE TABLE tab1 (
  `id` INTEGER,
  `a` INTEGER,
  `b` VARCHAR(4),
  `c` VARCHAR(4)
);

INSERT INTO tab1
  (`id`, `a`, `b`, `c`)
VALUES
  ('1', '100', 'b1', 'c1'),
  ('2', '104', 'b2', 'c2'),
  ('3', '106', 'b1', 'c1'),
  ('5', '118', 'b2', 'c2'),
   ('6', '120', 'b1', 'c1');
SELECT 
`id`, `a`, `b`, `c`,`id(1)`, `b(1)`, `c(1)`
FROM
(SELECT t1.id,t1.a,t1.b,t1.c
,t2.id `id(1)`
,t2.b as `b(1)`
,t2.c `c(1)`
, ROW_NUMBER() OVER(PARTITION BY t1.id ORDER BY t1.id,t2.id) rn
FROM tab1 t1 LEFT JOIN tab1 t2 ON t1.b = t2.b AND t1.c = t2.c and t1.id<t2.id) t3
WHERE rn = 1
id |   a | b  | c  | id(1) | b(1) | c(1)
-: | --: | :- | :- | ----: | :--- | :---
 1 | 100 | b1 | c1 |     3 | b1   | c1  
 2 | 104 | b2 | c2 |     5 | b2   | c2  
 3 | 106 | b1 | c1 |     6 | b1   | c1  
 5 | 118 | b2 | c2 |  null | null | null
 6 | 120 | b1 | c1 |  null | null | null

db<>fiddle here

MySQL 5.7 version

SELECT 
`id`, `a`, `b`, `c`,`id(1)`, `b(1)`, `c(1)`
FROM
(SELECT 
`id`, `a`, `b`, `c`,`id(1)`, `b(1)`, `c(1)`
,if (@t1id = id, @rn := @rn 1, @rn := 1) rn
, @t1id := id
FROM
(SELECT t1.id,t1.a,t1.b,t1.c
,t2.id `id(1)`
,t2.b as `b(1)`
,t2.c `c(1)`
FROM tab1 t1 LEFT JOIN tab1 t2 ON t1.b = t2.b AND t1.c = t2.c and t1.id<t2.id
ORDER BY t1.id,t2.id
LIMIT 100000) t5
,(SELECT @t1id := 0, @rn := 0) t4) t6
WHERE rn = 1
ORDER BY id
id |   a | b  | c  | id(1) | b(1) | c(1)
-: | --: | :- | :- | ----: | :--- | :---
 1 | 100 | b1 | c1 |     3 | b1   | c1  
 2 | 104 | b2 | c2 |     5 | b2   | c2  
 3 | 106 | b1 | c1 |     6 | b1   | c1  
 5 | 118 | b2 | c2 |  null | null | null
 6 | 120 | b1 | c1 |  null | null | null

db<>fiddle here

CodePudding user response:

You can use LATERAL, that makes things easy IMHO:

select t1.*, tt.id matched_id, tt.b b1, tt.c c1
 from myTable t1
 left outer join lateral 
 (select * from myTable t2
    where t1.b = t2.b and t1.c = t2.c and t2.a > t1.a
    limit 1
 ) tt on true

Here is DBFiddle demo.

EDIT: I didn't notice it was 5.7 before, sorry. Here is an 5.7 version:

select tmp.*, t3.b b1, t3.c c1
from (select id, b, c, 
  (select min(id) 
   from myTable t2 
   where t1.b = t2.b and t1.c = t2.c and t2.a > t1.a) as matched_id
 from myTable t1) tmp
 left join myTable t3 on t3.id = tmp.matched_id;

CodePudding user response:

This one worked with me:

SELECT p.id, p.a, p.b, p.c, matched_id.id,matched_id.b, matched_id.c 
FROM test p left join test matched_id on  
    matched_id.a = (select min(a) 
                    from test m 
                    where m.a>p.a and m.b=p.b and m.c = p.c);

There were some trial and error on this, so it's not so clean, sorry.

db-fiddle: https://www.db-fiddle.com/f/6w4NsFzCi6Qm4sfUgkKLgf/0

  • Related