在子查询中使用逐个命令

时间:2013-04-02 18:11:37

标签: mysql sql

每个时间表都有很多斜坡,每个斜坡都有一个end_date

我需要列出我的所有斜坡,以及一个额外的字段,其中包含斜坡的id以及该时间表的最新结束日期。我还需要能够在WHERE子句中使用这个额外的字段。

这通常是使用MAX函数的子查询的情况,除了一个问题:渐变的end_date字段可以为空,表示斜坡是当前的。因此,SELECT MAX(end_date)不起作用,因为null值比非空值“小”。

到目前为止,这是我提出的:

SELECT r1.*,
    (SELECT r2.id
    FROM ramp as r2
    WHERE schedule_id = r.schedule_id
    ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
    LIMIT 1) as latestId
FROM ramp as r1

这会产生这个表,这正是我想要的:

+-------+-------------+------------+-----------------+--------+------------+------------+----------+
| id    | schedule_id | suppr_flag | comment         | months | start_dte  | end_dte    | latestId |
+-------+-------------+------------+-----------------+--------+------------+------------+----------+
|    16 |           7 | NULL       | NULL            |   NULL | 2008-06-23 | NULL       |       16 |
|    15 |           6 | NULL       | NULL            |   NULL | 2007-05-01 | 2007-12-31 |       15 |
|    13 |           5 | NULL       | 1-15 deals      |   NULL | 2004-08-11 | NULL       |       13 |
|    11 |           4 | NULL       | NULL            |   NULL | 2005-08-11 | NULL       |       11 |
|    12 |           4 | NULL       | NULL            |     12 | 2004-08-11 | 2005-08-10 |       11 |
|    17 |          13 | NULL       | NULL            |      6 | 2009-03-05 | 2009-09-04 |       19 |
|    18 |          13 | NULL       | NULL            |      6 | 2009-09-05 | 2010-03-04 |       19 |
|    19 |          13 | NULL       | NULL            |   NULL | 2010-03-05 | NULL       |       19 |
|    20 |          14 | NULL       | NULL            |     12 | 2001-06-18 | 2008-06-17 |       20 |

除了我不能在WHERE子句中使用latestId(它是一个未知的列)。

你有什么想法吗?

2 个答案:

答案 0 :(得分:2)

快速解决方案是在ORDER BY子句上重复整个查询,因为在那里看不到别名,但我不太喜欢查询的外观:

SELECT r1.*,
    (SELECT r2.id
    FROM ramp as r2
    WHERE schedule_id = r1.schedule_id
    ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
    LIMIT 1) as latestId
FROM ramp as r1
ORDER BY
    (SELECT r2.id
    FROM ramp as r2
    WHERE schedule_id = r1.schedule_id
    ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
    LIMIT 1);

或者您可以从原始查询中进行选择,并订购结果:

SELECT s.*
FROM (
  SELECT r1.*,
      (SELECT r2.id
      FROM ramp as r2
      WHERE schedule_id = r1.schedule_id
      ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC
      LIMIT 1) as latestId
  FROM ramp as r1
) s
ORDER BY s.latestId

但如果我理解您的逻辑是正确的,那么您可以使用此查询为每个end_dte获取最大schedule_id

SELECT schedule_id, MAX(COALESCE(end_dte, '9999-12-31')) max_dte
FROM ramp
GROUP BY schedule_id;

然后,您可以再次使用ramp加入此查询,以获取与最大end_dte关联的ID。在ON子句中,您将再次使用COALESCE:

SELECT r1.schedule_id, r2.id as latestId
FROM (
  SELECT schedule_id, MAX(COALESCE(end_dte, '9999-12-31')) max_dte
  FROM ramp
  GROUP BY schedule_id) r1 INNER JOIN
  ramp r2
  ON r1.max_dte = COALESCE(r2.end_dte, '9999-12-31')
     AND r1.schedule_id = r2.schedule_id

然后你可以再加入这个以获得你需要的结果:

SELECT ramp.*, m.latestId
FROM
  ramp INNER JOIN (
    SELECT r1.schedule_id, r2.id as latestId
    FROM (
      SELECT schedule_id, MAX(COALESCE(end_dte, '9999-12-31')) max_dte
      FROM ramp
      GROUP BY schedule_id) r1 INNER JOIN
      ramp r2
      ON r1.max_dte = COALESCE(r2.end_dte, '9999-12-31')
         AND r1.schedule_id = r2.schedule_id
      ) m ON ramp.schedule_id = m.schedule_id
ORDER BY
  latestId

请参阅小提琴here。请注意,我使用的是'9999-12-31'而不是'9999-99-99',第一个是有效日期,第二个不是。

修改

如果您还想考虑多个schedule_id共享相同的最大日期,并且在这种情况下您只需要最新(最大)ID,则可以使用GROUP BY查询,以及MAX聚合函数:

SELECT r1.schedule_id, MAX(r2.id) as latestId
FROM (
  SELECT schedule_id, MAX(COALESCE(end_dte, '9999-12-31')) max_dte
  FROM ramp
  GROUP BY schedule_id) r1 INNER JOIN
  ramp r2
  ON r1.max_dte = COALESCE(r2.end_dte, '9999-12-31')
     AND r1.schedule_id = r2.schedule_id
GROUP BY
  r1.schedule_id

并在主查询中使用此更新版本。

答案 1 :(得分:0)

您可以在订单中使用两列...首先是非空指示标志,然后是日期。改变你的......

ORDER BY IF(end_dte is NULL, '9999-99-99', end_dte) DESC

ORDER BY
   IF( end_dte is null, 1, 2 ),
   end_dte DESC

这样,它将所有“NULL”结束日期推送到列表的顶部(通过IF()值将是1对2对于其他任何有日期的东西),然后,按日期降序