Я думаю, что ваша модель данных имеет недостатки.В настоящее время ваша модель имеет несколько записей на комнату, по одной на слот.Поскольку ваш запрос ограничивает число учеников только комнатами, а не слотами, возникает перекрестное объединение, что является неправильным результатом.
Можно выполнить запрос, чтобы преодолеть недостатки модели.Ключевое слово DISTINCT является тупым инструментом выбора в этих сценариях:
SQL> select *
2 from ( select DISTINCT dorm_building, dorm_room from webRooms) wR
3 LEFT JOIN residency R
4 on wR.dorm_building = r.dorm_building
5 and wr.dorm_room = r.dorm_room
6 /
DORM_BUILDING DORM_ROOM STUDENT_ID DORM_BUILDING DORM_ROOM
-------------------- ---------- ---------- -------------------- ----------
my_dorm 1 123 my_dorm 1
my_dorm 1 345 my_dorm 1
my_dorm 2
SQL>
Лучший способ справиться с этим - использовать таблицу SLOTS.Это устраняет необходимость иметь несколько записей WEBROOMS для представления одной физической комнаты.Вы говорите, что это «несущественно», какой слот назначен Студенту, но это ключ к успешной работе приложения, что Студент назначен для определенного Слота.
Вот некоторые доказательства концептуальных таблиц:
create table webrooms
(dorm_building varchar2(20)
, dorm_room number)
/
create table slots
(dorm_building varchar2(20)
, dorm_room number
, occupant_num number)
/
create table residency
(student_id number
, dorm_building varchar2(20)
, dorm_room number
, occupant_num number)
/
Как видите, в пересмотренном запросе четко указывается, какие слоты заняты, а какие остаются свободными:
SQL> select wr.*, s.occupant_num, r.student_id
2 from webrooms wr
3 INNER JOIN slots s
4 on wr.dorm_building = s.dorm_building
5 and wr.dorm_room = s.dorm_room
6 LEFT JOIN residency r
7 on s.dorm_building = r.dorm_building
8 and s.dorm_room = r.dorm_room
9 and s.occupant_num = r.occupant_num
10 order by 1, 2, 3, 4
11 /
DORM_BUILDING DORM_ROOM OCCUPANT_NUM STUDENT_ID
-------------------- ---------- ------------ ----------
my_dorm 1 1 123
my_dorm 1 2 345
my_dorm 2 1 678
my_dorm 2 2
my_dorm 2 3 890
my_dorm 3 1
my_dorm 3 2
my_dorm 3 3
my_dorm 4 1
my_dorm 4 2 666
9 rows selected.
SQL>
Или, если у нас есть база данных, которая поддерживает запросы PIVOT(Я использую Oracle 11g здесь):
SQL> select * from (
2 select wr.dorm_building||' #'||wr.dorm_room as dorm_room
3 , num_gen.num as slot_number
4 , case
5 when r.student_id is not null then r.student_id
6 when s.occupant_num is not null then 0
7 else null
8 end as occupancy
9 from webrooms wr
10 CROSS JOIN ( select rownum as num from dual connect by level <= 4) num_gen
11 LEFT JOIN slots s
12 on wr.dorm_building = s.dorm_building
13 and wr.dorm_room = s.dorm_room
14 and num_gen.num = s.occupant_num
15 LEFT JOIN residency r
16 on s.dorm_building = r.dorm_building
17 and s.dorm_room = r.dorm_room
18 and s.occupant_num = r.occupant_num
19 )
20 pivot
21 ( sum (occupancy)
22 for slot_number in ( 1, 2, 3, 4)
23 )
24 order by dorm_room
25 /
DORM_ROOM 1 2 3 4
---------- ---------- ---------- ---------- ----------
my_dorm #1 123 345
my_dorm #2 678 0 890
my_dorm #3 0 0 0
my_dorm #4 0 666
SQL>