Присоединение к столу один-ко-многим? - PullRequest
0 голосов
/ 15 июля 2010

У меня есть одна таблица (webRooms), в которой есть ряд строк, совпадающих со строками во второй таблице (Residency).Таблица 1 выглядит следующим образом:

ID  |  dorm_building  |  dorm_room |  occupant_num

Таблица 2 выглядит следующим образом:

student_ID  |  dorm_building  | dorm_room

Мне бы хотелось получить следующие результаты:

    ID  | dorm_building  | dorm_room  | occupant_num  | student_id
    1   |  my_dorm       | 1          | 1             | 123
    2   | my_dorm        | 1          | 2             | 345

Но то, что я сейчас получаю, выглядит так:

    ID  | dorm_building  | dorm_room  | occupant_num  | student_id
    1   |  my_dorm       | 1          | 1             | 123
    2   | my_dorm        | 1          | 2             | 123

Я сейчас использую левое соединение, есть предложения?

Текущий запрос выглядит так:

select * from webRooms wR 
  LEFT JOIN RESIDENCY R on wR.dorm_building = r.DORM_BUILDING 
    and wr.dorm_room = r.DORM_ROOM 

Из-за некоторых ответов я добавляю в таблицу третью таблицу.Эта таблица существует - это то, что я использую для создания таблицы webRooms, она называется webDorms и выглядит так:

ID |общежитие |комната в общежитии |max_occupancy

Он имеет такие результаты:

2 |my_dorm |1 |2

Ответы [ 2 ]

2 голосов
/ 15 июля 2010

Я думаю, что ваша модель данных имеет недостатки.В настоящее время ваша модель имеет несколько записей на комнату, по одной на слот.Поскольку ваш запрос ограничивает число учеников только комнатами, а не слотами, возникает перекрестное объединение, что является неправильным результатом.

Можно выполнить запрос, чтобы преодолеть недостатки модели.Ключевое слово 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>
1 голос
/ 15 июля 2010

Вы упомянули в комментариях к сообщению APC, что все, что вам нужно, это подсчет доступности.Если это действительно так, то я думаю, что следующий вариант будет более эффективным:

Create Table Rooms  (
                        dorm_building ... Not Null
                        , dorm_room ... Not Null
                        , capacity int Not Null default ( 0 )
                        , Constraint PK_Rooms Primary Key ( dorm_building, dorm_room )
                        , ...
                        )

Create Table Residency  (
                            student_id ... Not Null Primary Key
                            , dorm_building ... Not Null
                            , dorm_room ... Not Null
                            , Constraint FK_Residency_Rooms
                                Foreign Key ( dorm_building, dorm_room )
                                References Rooms ( dorm_building, dorm_room )
                            , ...
                            )

Я сделал student_id первичным ключом в таблице Residency только потому, что нет упоминания оэлемент времени, и ученик не должен находиться в двух комнатах одновременно.Теперь, чтобы получить доступное пространство, мы можем сделать:

Select Rooms.dorm_building, Rooms.dorm_room
    , Rooms.Capacity
    , Coalesce(RoomCounts.OccupantTotal,0) As TotalOccupants
    , Rooms.Capacity - Coalesce(RoomCounts.OccupantTotal,0) As AvailableSpace
From Rooms
    Left Join   (
                Select R1.dorm_building, R1.dorm_room, Count(*) As OccupantTotal
                From Residency As R1
                Group By R1.dorm_building, R1.dorm_room
                ) As RoomCounts
        On RoomCounts.dorm_building = Rooms.dorm_building
            And RoomCounts.dorm_room = Rooms.dorm_room

Теперь, если вы также хотите отображать «слоты», вы должны рассчитать это на лету (это предполагает SQL Server 2005 и более поздние версии):

With Numbers As
    (
    Select Row_Number() Over ( Order By C1.object_id ) As Value
    From sys.columns As C1
        Cross Join sys.columns As C2
    )
    , NumberedResidency As
    (
    Select dorm_building, dorm_room, student_id
        , Row_Number() Over ( Partition By dorm_building, dorm_room Order By student_id ) As OccupantNum
    From Residency
    )
Select Rooms.dorm_building, Rooms.dorm_room, R.OccupantNum, R.StudentId
From Rooms
    Join Numbers As N
        On N.Value <= Rooms.Capacity
    Left Join NumberedResidency As R
        On R.dorm_building = Rooms.dorm_building
            And R.dorm_room = Rooms.dorm_room
            And N.Value = R.OccupantNum
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...