Frage

I have a query where I perform three left outer joins, but I'd like to return only one result.

I asked this question awhile back: Multiple Outer joins return top result

Adding to that, I am having trouble returning the top row that is several joins away.

For example:

obj

|id    |  name  |
 1     |  bear  |
 2     |  cat   |
 3     |  moose |


obj_cn

|id    | obj_id | lex_id | ord
| 1    |  2     |  1     |  3
| 2    |  2     |  2     |  2
| 3    |  2     |  3     |  1
| 4    |  1     |  4     |  1

lex

| id   | trm_id |
| 1    |  2     |
| 2    |  1     |
| 3    |  4     |
| 4    |  3     |

trm

| id   | trm |
| 1    | puma|
| 2    | lion|
| 3    | brn |
| 4    |  cgr|

Ideally, I would like to return the lowest ordered term, along with other columns in the objtermktr table (and other joins, this example is simplified).

results

|id    | name   | trm
|1     | bear   | brn
|2     | cat    | cgr
|3     | moose  | NULL

My initial idea was to build off of the aforementioned question, but I am having trouble comparing across multiple joins without either returning a NULL value for trm or returning object id 2 three times (once for each term).

This query doesn't work, but here's sort of what I was trying

SELECT id, name ,
,(select trm from trm t where t.id = a.id and r.ord=a.ord7) as term
from obj

left outer join
(select obj_id, lex_id, MIN(ord) ord7 from obj_cn group by obj_id, lex_id) a
on obj.id = a.obj_id

left outer join lex on a.lex_id = lex.trm_id
left outer join trm on lex.trm_id = trm.id)

Any guidance would be appreciated. Thanks for reading!

UPDATE: I didn't make this database and cannot restructure it.

War es hilfreich?

Lösung

It helps a lot if you include CREATE TABLE and INSERT statements.

create table obj (
  id integer primary key,
  name varchar(10) not null unique
  );

insert into obj values
(1, 'bear'),
(2, 'cat'),
(3, 'moose');

create table trm (
  id integer primary key,
  trm varchar(10) not null unique
  );

insert into trm values
(1, 'puma'),
(2, 'lion'),
(3, 'brn'),
(4, 'cgr');

create table lex (
  id integer primary key,
  trm_id integer not null 
    references trm (id)
);

insert into lex values
(1, 2),
(2, 1),
(3, 4),
(4, 3);

create table obj_cn (
  id integer primary key,
  obj_id integer not null
    references obj (id),
  lex_id integer not null
    references lex (id),
  ord integer not null
    check (ord > 0),
  unique (obj_id, lex_id)
);

insert into obj_cn values
(1, 2, 1, 3),
(2, 2, 2, 2),
(3, 2, 3, 1),
(4, 1, 4, 1);

Ideally, I would like to return the lowest ordered term, along with other columns in the objtermktr table (and other joins, this example is simplified).

It sounds like this gives you the lowest ordered term.

select obj_id, min(ord) as ord
from obj_cn
group by obj_id;

This slightly more complex version will preserve all the rows in the "obj" table, which you'll need in order to get the result set you want.

select obj.id as obj_id, min(ord) as ord
from obj
left join obj_cn on obj.id = obj_cn.obj_id
group by obj.id

You can use either of those derived tables in a join (or in multiple joins) on "obj_id" or on both "obj_id" and "ord". In the example below, I use the second of statement (above) in a common table expression, and join on both columns.

with min_ords as (
  select obj.id as obj_id, min(ord) as ord
  from obj
  left join obj_cn on obj.id = obj_cn.obj_id
  group by obj.id
)
select obj.id, obj.name, trm.trm
from obj
left join min_ords on min_ords.obj_id = obj.id 
left join obj_cn on obj_cn.obj_id = obj.id and obj_cn.ord = min_ords.ord
left join lex on lex.id = obj_cn.lex_id
left join trm on trm.id = lex.trm_id
order by id

1  bear   brn
2  cat    cgr
3  moose

Since you surely have a unique constraint on obj.name and trm.trm, you could radically simplify your life by restructuring the obj_cn table. Reference "name" and "trm" directly. (I'm ignoring the "lex" table, the purpose of which is simply baffling.)

name   trm   ord
--
cat    lion  3
cat    puma  2
cat    cgr   1
bear   brn   1

Depending on the purpose, you might not need any joins for some queries that target that table.

Andere Tipps

Arg, I was putting the join in the wrong place. Still learning...

select obj.id, obj.name,
 (select trm.trm  from obj_cn F
 left outer join lex on f.lex_id = lex.id
 left outer join trm on lex.trm_id = trm.id
 where f.obj_id=C.obj_id and f.ord=C.ord7)

from obj

left outer join (select obj_id, MIN(ord) ord7 from obj_cn group by obj_id) C
on obj.id = C.obj_id

Hope this helps someone!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top