连接表上的Where子句

时间:2016-02-26 13:16:35

标签: sql postgresql

对于下表:

房间

+----+--------+
| 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-01-06 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_STATE ID 2OPEN

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

5 个答案:

答案 0 :(得分: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;

答案 1 :(得分: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

这将返回与“开放”状态相关的所有房间与“关闭”状态无关,该状态的日期位于“开放”状态的日期之后。< / p>

答案 2 :(得分: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;

答案 3 :(得分:0)

有几种方法可以给这只猫上皮。 @ GiorgosBetsos&#39;答案是一种方式。另:

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
    )

答案 4 :(得分: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