2016-02-26 48 views
3

对于下表:凡在连接表子句

ROOM

+----+--------+ 
| ID | NAME | 
+----+--------+ 
| 1 | ROOM_1 | 
| 2 | ROOM_2 | 
+----+--------+ 

ROOM_STATE

+----+---------+------+------------------------+ 
| ID | ROOM_ID | OPEN |   DATE   | 
+----+---------+------+------------------------+ 
| 1 |  1 | 1 | 2000-01-01 00:00:00 | 
| 2 |  2 | 1 | 2000-01-01 00:00:00 | 
| 3 |  2 | 0 | 2000-01-06 00:00:00 | 
+----+---------+------+------------------------+ 

存储的数据的客房与去年变化状态:

  • ROOM_1开于2000-01-01 00:00:00
  • ROOM_2在2000-01-01 00:00:00
  • ROOM_2开于2000年1月6日00:00:00
  • 关闭

ROOM_1仍然开放,ROOM_2已关闭(自2000-01-06开始不开放)。如何选择实际打开的房间名称加入?如果我写道:

SELECT ROOM.NAME 
FROM ROOM 
    INNER JOIN ROOM_STATE ON ROOM.ID = ROOM_STATE.ROOM_ID 
WHERE ROOM_STATE.OPEN = 1 

ROOM_1和ROOM_2选择,因为ROOM_STATEID2OPEN

SQL小提琴:http://sqlfiddle.com/#!9/68e8bf/3/0

+1

标签您与您正在使用的数据库的问题。 –

+0

完成。它不能是纯粹的SQL答案? – bux

+0

日期函数在SQL世界中通常是特定于供应商的。此外,有些产品不支持窗口化函数和/或通用表表达式,甚至不支持ANSI标准中的部分。如果你想要一个数据库不可知的答案,那么你可以特别要求一个,但它可能不如为你正在使用的特定数据库量身定制的那个最佳。 –

回答

3

在Postgres里,我会建议distinct on

select distinct on (rs.room_id) r.name, rs.* 
from room_state rs join 
    room r 
    on rs.room_id = r.id 
order by rs.room_id, rs.date desc; 

distinct on是特定Postgres的。它保证每个房间的结果只有一行(这是你想要的)。所选行是遇到的第一行,因此这将选择具有最大日期的行。

另一个有趣的方法是使用横向联接:

select r.*, rs.* 
from room r left join lateral 
    (select rs.* 
     from room_state rs 
     where rs.room_id = r.id 
     order by rs.date desc 
     fetch first 1 row only 
    ) rs; 
+0

嗨。我想保持联接。我把它添加到问题中。 – bux

+0

@bux。 。 。这只会增加一个额外的连接(和'select'中的列)。 –

2

您可以使用下面的查询:

SELECT R.ID, R.NAME 
FROM ROOM AS R 
INNER JOIN ROOM_STATE AS RS ON R.ID = RS.ROOM_ID AND RS.OPEN = 1 
LEFT JOIN ROOM_STATE AS RS2 ON R.ID = RS2.ROOM_ID AND RS2.OPEN = 0 AND RS2.DATE > RS.date 
WHERE RS2.ID IS NULL 

Demo here

这将返回相关的 '开放' 状态都客房没有与“开放”状态的日期之后的“关闭”状态的关系。

0

如果你能保证每个打开你有一个密切的,这始终是这种情况,你也可以使用这个疯狂的SQL;)

SELECT r.ID, r.NAME 
FROM ROOM r 
WHERE (select sum(CASE WHEN OPEN = 0 THEN -1 ELSE 1 END) from room_state rs where rs.room_id=r.id) = 1; 
0

这种猫有几种方法。 @GiorgosBetsos的答案是一种方式。另:

WITH Numbered_Status AS 
(
    SELECT 
     id, 
     room_id, 
     open, 
     ROW_NUMBER() OVER (PARTITION BY room_id ORDER BY date DESC) AS row_num 
    FROM 
     Room_State 
) 
SELECT 
    R.id, 
    R.name, 
    S.open 
FROM 
    Numbered_Status S 
INNER JOIN Room R ON R.room_id = S.room_id 
WHERE 
    row_num = 1 AND 
    open = 1 

您还可以使用NOT EXISTS

SELECT 
    R.id, 
    R.name, 
    S.open 
FROM 
    Room_State S 
INNER JOIN Room R ON R.id = S.room_id 
WHERE 
    S.open = 1 AND 
    NOT EXISTS 
    (
     SELECT * 
     FROM Room_State S2 
     WHERE 
      S2.room_id = S.room_id AND 
     S2.date > S.date 
    ) 
0

可能的解决方案,连接条件是通缉ROOM_STATE行:

SELECT R.ID, R.NAME 
FROM ROOM AS R 
INNER JOIN ROOM_STATE AS RS ON R.ID = RS.ROOM_ID 
    AND RS.DATE = (
    SELECT DATE 
    FROM ROOM_STATE 
    WHERE ROOM_ID = R.ID 
    ORDER BY DATE DESC 
    LIMIT 1 
) 
WHERE RS.OPEN = 1