Sql 联接表上的Where子句

Sql 联接表上的Where子句,sql,postgresql,Sql,Postgresql,有关下表: 房间 +----+--------+ | ID | NAME | +----+--------+ | 1 | ROOM_1 | | 2 | ROOM_2 | +----+--------+ 房间状态 +----+---------+------+------------------------+ | ID | ROOM_ID | OPEN | DATE | +----+---------+------+------------------

有关下表:

房间

+----+--------+
| ID |  NAME  |
+----+--------+
|  1 | ROOM_1 |
|  2 | ROOM_2 |
+----+--------+
房间状态

+----+---------+------+------------------------+
| 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 |
+----+---------+------+------------------------+
存储的数据是具有上次更改状态的文件室:

  • _1房间于2000-01-01 00:00开放
  • 2号房间于2000-01-01 00:00开放
  • _2房间于2000-01-06 00:00关闭
房间1仍然开放,房间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
之所以选择房间1和房间2,是因为带有
ID
2
房间状态
处于打开状态


SQL FIDLE:

您可以使用以下查询:

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 


这将返回所有与“打开”状态相关且与日期晚于“打开”状态日期的“关闭”状态无关的房间。

您可以使用以下查询:

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 


这将返回与“打开”状态相关的所有房间,并且与日期晚于“打开”状态日期的“关闭”状态无关。

在Postgres中,我建议
上进行区分:

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;

在Postgres中,我建议您在
上使用distinct:

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;

如果您可以保证每次打开都有一个关闭,并且总是这样,那么您也可以使用这个疯狂的sql;)


如果您可以保证每次打开都有一个关闭,并且总是这样,那么您也可以使用这个疯狂的sql;)


有几种方法可以剥这只猫的皮@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
您还可以使用
不存在

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
    )

有几种方法可以剥这只猫的皮@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
您还可以使用
不存在

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
    )

可能的解决方案,其中连接条件位于想要的房间\状态行上:

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

可能的解决方案,其中连接条件位于想要的房间\状态行上:

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

用您正在使用的数据库标记您的问题。完成。它不可能是纯sql答案?在sql世界中,日期函数通常是特定于供应商的。此外,有些产品不支持窗口函数和/或通用表表达式,甚至不支持ANSI标准中的部分。如果您想要一个与数据库无关的答案,那么您可以要求一个具体的答案,但它可能不如针对您正在使用的特定数据库定制的答案那么理想。@GordonLinoff感谢您的精确性!用您正在使用的数据库标记您的问题。完成。它不可能是纯sql答案?在sql世界中,日期函数通常是特定于供应商的。此外,有些产品不支持窗口函数和/或通用表表达式,甚至不支持ANSI标准中的部分。如果您想要一个与数据库无关的答案,那么您可以要求一个具体的答案,但它可能不如针对您正在使用的特定数据库定制的答案那么理想。@GordonLinoff感谢您的精确性!你好我想继续加入。我把它添加到问题中。@bux。这只会添加一个额外的联接(以及
选择中的列)。您好。我想继续加入。我把它添加到问题中。@bux。这只会添加一个额外的联接(以及
选择中的列)。