简体   繁体   中英

Sql complex group by query (oracle jpa)

id     let      num    dir    ctry
----------------------------------
0       A       10      W      US   <- first row of route (A-W)
1       A       20      W      CA
2       A       30      W      MX
3       B       25      E      US
4       B       30      E      CA
5       B       20      E      CA   <- first row of route (B-E)

I have a table which is composed of segments (1 row = 1 segment) and routes (multiple segments).

A route is composed of segments having the same "let" and "dir" combination. So the first 3 rows is one route, and the last 3 rows is another.

The first segment in the route is the row with the smallest num .

  • So, row id = 0 is the first segment in route 1,
  • and the last row id = 5 is the first segment in route 2.

I want to return the rows that make up a route based on the 1st segment of that route equaling a given country.

If I filtered on a "ctry" of CA the result would be the following:

id     let    num     dir   ctry
--------------------------------
3       B      25      E     US
4       B      30      E     CA
5       B      20      E     CA
 SELECT * 
 FROM YourTable 
 WHERE (let, dir) IN (SELECT distinct let, dir
                      FROM YourTable Y
                      WHERE Y.ctry = 'CA')

OR

 SELECT L.*
 FROM YourTable L
 JOIN (SELECT distinct let, dir
       FROM YourTable Y
       WHERE Y.ctry = 'CA') R
   ON L.let = R.let
  AND L.dir = R.dir

Based on your comment SqlFiddleDemo

SELECT R.*
FROM 
     (SELECT let, dir, min(num) as minnum
      FROM Routes t
      GROUP BY let, dir
     )  minT
JOIN  (SELECT t.*
       FROM Routes t      
      )  firstrow
   ON minT.let = firstrow.let
  AND minT.dir = firstrow.dir
  AND minT.minnum = firstrow.num
JOIN  Routes R
   ON firstRow.let = R.let
  AND firstRow.dir = R.dir
  AND firstRow.Ctry = 'CA';

In Oracle, you can do this using analytics function:

select t.*
from (select t.*,
             min(country) keep (dense_rank first order by num) over (partition by let, dir) as first_country
      from t
     ) t
where first_country = 'CA';

JPA is more limited. This might work:

select t.*
from t join
     (select let, dir, min(num) as minnum
      from t
      group by let, dir
     ) ld 
     on t.let = ld.let and t.dir = ld.dir join
     t tt
     on tt.let = ld.let and tt.dir = ld.dir and tt.num = ld.minnum
where tt.country = 'CA';

http://sqlfiddle.com/#!9/ae58a/1

By using your guys direction I came up with a good solution that is readable. Thanks juan and gordon.

SELECT Routes.* FROM Routes
JOIN (SELECT let,dir,min(num) as minnum
      FROM Routes
      GROUP BY let, dir
      ) ld JOIN Routes tt
ON tt.let = ld.let AND tt.dir = ld.dir AND tt.num = ld.minnum 
AND Routes.let = ld.let AND Routes.dir = ld.dir
WHERE tt.ctry = 'CA';

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM