【问题标题】:Return the record with the date closest to but greater than now NOW() in table b, for each record in table a对于表a中的每条记录,返回表b中日期最接近但大于now()的记录
【发布时间】:2020-02-06 07:59:16
【问题描述】:

服务器版本:10.3.15-MariaDB-log

我有这样的数据结构

TABLE A - Participant

participantID
--------------
1
2
3
4

TABLE B - Appointment

appointmentID | participantID | locationID | beginDateTime
----------------------------------------------------------------
1             | 1             | 1          | 2019-10-09 11:00:00
2             | 1             | 1          | 2019-10-10 11:00:00
3             | 2             | 2          | 2019-10-11 11:00:00
4             | 3             | 3          | 2019-11-09 11:00:00
5             | 5             | 1          | 2019-10-15 11:00:00

TABLE C - Location

locationID | locationTypeID
----------------------------
1          | 1
2          | 2
3          | 3

TABLE D - Location Type

locationTypeID | locationType
-----------------------------
1              | mobile
2              | onsite
3              | unknown

我只想获取未来约会的参与者,并且如果他们未来约会的位置是移动位置类型,我只想返回这些参与者。但是我只关心他们最近的约会地点。我需要在单个查询中执行此操作。我已经到了这个阶段,在这里我可以获取所有位置的 locationType,对于所有比现在更大的约会,对于所有人,但我需要将其限制为仅在未来最近的约会并且不知道如何继续。

SELECT p.participantID
FROM locationType AS lt
LEFT JOIN location AS l ON l.locationTypeID = lt.locationTypeID
LEFT JOIN appointment AS a ON a.locationID = l.locationID
LEFT JOIN participant AS p ON p.participantID = a.participantID
WHERE p.participantID IN (
    SELECT a.participantID
    FROM appointment AS a
    LEFT JOIN location AS l ON l.locationID = a.locationID
    LEFT JOIN locationType AS lt ON lt.locationTypeID = l.locationTYpeID
    WHERE a.beginDateTime > NOW()
    AND l.locationTypeID IN (
        SELECT locationTypeID 
        FROM locationType 
        WHERE locationType = 'mobile'
    )
);

【问题讨论】:

  • 你的 MySQL 服务器版本是多少?请运行Select Version(); 并报告此查询的结果
  • @MadhurBhaiya 添加了

标签: mysql sql mariadb greatest-n-per-group mariadb-10.3


【解决方案1】:

MariaDB 10.3 支持Window Functions;因此,一种方法是根据您的要求简单地加入表,然后按beginDateTime 的升序计算行号。之后,您可以将结果用作子查询,并仅考虑行号为 1 的那些行。

ROW_NUMBER() OVER(PARTITION BY p.participantID ORDER BY a.beginDateTime ASC) :它将所有行分区在一起,具有相同的p.participantID 值。就像GROUP BY,但这里的主要区别在于GROUP BY 将它们聚合成一行,而PARTITION BY 将它们维护为单独的行。现在,在特定值p.participantID 的特定分区内,它按beginDateTime 值的升序将行号分配给各个行。因此,beginDateTime 值最低的行获得行号 = 1,第二低为 2,依此类推。由于您想要最近的日期时间行,因此我们可以简单地考虑行号为 1 的行。

SELECT * FROM 
(
  SELECT p.participantID, 
         ROW_NUMBER() OVER(PARTITION BY p.participantID 
                           ORDER BY a.beginDateTime ASC) AS rn
  FROM locationType AS lt
  JOIN location AS l ON l.locationTypeID = lt.locationTypeID
  JOIN appointment AS a ON a.locationID = l.locationID 
                           AND a.beginDateTime > NOW() 
  JOIN participant AS p ON p.participantID = a.participantID 
  WHERE lt.locationType = 'Mobile'
) dt 
WHERE rn = 1

【讨论】:

  • 你能解释一下这行在做什么吗:ROW_NUMBER() OVER(PARTITION BY p.participantID ORDER BY a.beginDateTime ASC) AS rn 这对我来说是全新的 SQL 语法
  • @Xenology 为答案添加了更多解释(检查第二段)。如果还不清楚,请告诉我。
  • 太棒了,非常感谢。非常清晰简洁。
猜你喜欢
  • 1970-01-01
  • 2011-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-05
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
相关资源
最近更新 更多