2010-07-15 6 views
0

J'ai une table (webRooms) qui a un nombre de rangées qui correspondent à celles d'une deuxième table (résidence). Le tableau 1 ressemble à ceci:Un-à-plusieurs Table Join?

ID | dorm_building | dorm_room | occupant_num 

Tableau 2 ressemble à ceci:

student_ID | dorm_building | dorm_room 

Ce que je voudrais est d'obtenir des résultats comme celui-ci:

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

Mais ce que je reçois actuellement ressemble à ceci:

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

I ' m utilise actuellement une jointure gauche, des suggestions?

requête actuelle ressemble à ceci:

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

En raison de quelques-unes des réponses données J'ajoute une troisième table dans le mélange. Cette table a existé - c'est ce que j'utilise pour générer la table webRooms, elle s'appelle webDorms et ressemble à ceci:

ID | dortoir | dortoir | max_occupancy

Il a des résultats comme celui-ci:

2 | my_dorm | 1 | 2

+2

@davemackey Qu'est-ce que vous utilisez actuellement pour votre requête – msarchet

+0

@msarchet: select * from webRooms wR LEFT JOIN R sur RÉSIDENTS wR.dorm_building = r.DORM_BUILDING et wr.dorm_room = r.DORM_ROOM – davemackey

+0

avez-vous vérifié vos données ? peut-être que vous avez deux fois le même enregistrement dans la table 'Residency'. –

Répondre

2

Je pense que votre modèle de données est défectueux. Actuellement, votre modèle a plusieurs enregistrements par pièce, un par emplacement. Parce que votre requête limite uniquement les Etudiants aux Chambres et non aux Machines à sous, elle produit une jointure croisée, ce qui est le mauvais résultat.

Il est possible de clarifier votre requête pour pallier les défauts du modèle. Le mot-clé DISTINCT est l'instrument contondant de choix dans ces scénarios:

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> 

Une meilleure façon d'aborder ce serait avec une table SLOTS. Cela supprime le besoin d'avoir plusieurs enregistrements WEBROOMS pour représenter une seule pièce physique. Vous dites qu'il est "inconséquent" auquel un étudiant est assigné, mais il est essentiel pour le bon fonctionnement de l'application qu'un étudiant soit affecté à un emplacement spécifique.

Voici une preuve de tables concept:

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) 
/

Comme vous pouvez le voir, la requête révisée fournit des indications claires dont emplacements sont occupés et qui reste libre:

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> 

Ou, si nous avons une base de données qui prend en charge les requêtes PIVOT (j'utilise Oracle 11g ici):

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> 
+0

Un étudiant n'est pas et ne devrait pas être attaché à un emplacement spécifique dans une pièce. J'ai juste besoin d'un moyen de démontrer si une pièce est pleine ou pas - et visuellement combien de fentes restent. occupant_num est juste un espace réservé indiquant que cet enregistrement est pour x slot dans la pièce - mais l'emplacement spécifique est sans conséquence. – davemackey

+0

Le problème avec ceci est que toutes les pièces ne sont pas entièrement occupées - mais j'ai besoin d'afficher un espace réservé vide signifiant ce fait. Ainsi, dans certaines pièces, il se peut qu'un seul emplacement soit occupé à l'heure actuelle, mais je souhaite que le visualiseur de rapports voit qu'il existe un autre emplacement disponible. De plus, certaines chambres ont plus de fentes - par ex. cinq. – davemackey

+1

@DaveMackey - Deux de mes suggestions vous fourniront des espaces réservés: soit vous changez votre modèle de données pour avoir une table SLOTS séparée, soit vous conservez votre modèle actuel et effacez simplement la requête. – APC

1

Vous avez mentionné dans les commentaires au poste d'APC que tout ce que vous voulez, c'est le nombre de disponibilité.Si tel est effectivement le cas, alors je pense que ce qui suit serait une conception plus efficace:

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) 
          , ... 
          ) 

J'ai fait student_id la clé primaire dans la seule table Residency parce qu'il n'y a aucune mention d'un élément de temps et il shouldn Un étudiant ne peut pas être dans deux pièces en même temps. Maintenant, pour obtenir l'espace disponible, nous pouvons faire:

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 

Maintenant, si vous voulez aussi afficher « slots », vous devez calculer que à la volée (cela suppose SQL Server 2005 et versions ultérieures):

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