我有一个表(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 | dorm_building | dorm_room | max_occupancy
结果如下:
2 | my_dorm | 1 | 2
答案 0 :(得分:2)
我认为您的数据模型存在缺陷。目前,您的模型每个房间有多个记录,每个插槽一个。因为您的查询仅限制学生到房间而不是插槽,所以它会产生交叉连接,这是错误的结果。
可以克服您的查询以克服模型的缺点。在这些情况下,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记录来表示单个物理空间。你说学生被分配给Slot是“无关紧要的”,但它是成功运作学生被分配到特定Slot的关键。
以下是概念表的一些证明:
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 :(得分:1)
你在对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