选择MySQL

时间:2017-08-25 11:43:40

标签: mysql subquery

假设我有一个这样的表:

| id |       date | name | value |
|----|------------|------|-------|
|  0 | 2017-01-14 |  foo |   one |
|  1 | 2017-01-17 |  bar |   two |
|  2 | 2017-01-18 | john |  five |
|  3 | 2017-01-19 |  doe |   ten |

(其中date无需订购)

我希望能够选择上一个行的某些值(基于date)。可以通过以下查询来实现这样的功能:

SELECT 
  *, 
  (SELECT 
     name 
   FROM 
     example e2 
   WHERE 
     e2.dt < e1.dt 
   ORDER BY dt DESC 
   LIMIT 1
   ) as prev_name 
FROM example e1

结果表:

| id |         dt | name | value | prev_name |
|----|------------|------|-------|-----------|
|  0 | 2017-01-14 |  foo |   one |    (null) |
|  1 | 2017-01-17 |  bar |   two |       foo |
|  2 | 2017-01-18 | john |  five |       bar |
|  3 | 2017-01-19 |  doe |   ten |      john |

现在,这很好用。但是,如果我可以轻松地从上一行中选择多个列,最好会产生如下结果:

| id |         dt | name | value | prev_name | prev_value |    prev_dt |
|----|------------|------|-------|-----------|------------|------------|
|  0 | 2017-01-14 |  foo |   one |    (null) |     (null) |     (null) |
|  1 | 2017-01-17 |  bar |   two |       foo |        one | 2017-01-14 |
|  2 | 2017-01-18 | john |  five |       bar |        two | 2017-01-17 |
|  3 | 2017-01-19 |  doe |   ten |      john |       five | 2017-01-18 |

这当然可以通过简单地将子查询(SELECT [..] FROM example e2 ...)多次复制到查询中来完成,但我想这不是最好的方法。我发现了几个关于SO寻址的问题 “如何从前一行选择记录”“如何使用子查询选择多列”问题,但不是两者都有。后一个问题通常使用JOIN语句来解决,但我认为这不能与“前一行”的情况相结合。所以我的问题是:什么是产生最后结果的更好方法,而不是为我们需要的每一列复制子查询?

编辑。作为额外的约束,我没有在原始问题中包含,“previous”实际上可能与前一行不同,而是“满足条件的前一行” ”。因此,假设我的表格包含额外的booleanb

| id |         dt | name | value | b |
|----|------------|------|-------|---|
|  0 | 2017-01-14 |  foo |   one | 1 |
|  1 | 2017-01-17 |  bar |   two | 0 |
|  2 | 2017-01-18 | john |  five | 1 |
|  3 | 2017-01-19 |  doe |   ten | 0 |

我希望“上一行”成为b = 1的上一行,因此所需的结果将是:

| id |         dt | name | value | b | prev_name | prev_value |    prev_dt |
|----|------------|------|-------|---|-----------|------------|------------|
|  0 | 2017-01-14 |  foo |   one | 1 |    (null) |     (null) |     (null) |
|  1 | 2017-01-17 |  bar |   two | 0 |       foo |        one | 2017-01-14 |
|  2 | 2017-01-18 | john |  five | 1 |       foo |        one | 2017-01-14 |
|  3 | 2017-01-19 |  doe |   ten | 0 |      john |       five | 2017-01-18 |

我认为这仍然可以通过James Scott的答案来完成,只需使用b = 1语句在IF时更新变量,但在这种情况下可能还有其他解决方案。< / p>

编辑。 SQLfiddle

2 个答案:

答案 0 :(得分:1)

如果您只想要上一行,看起来像是会话变量的一个很好的用例,您可以使用ORDER BY来获得不同的结果。

SET @VDt := NULL, @VName := NULL, @VValue := NULL;

SELECT  id, @VName prev_name, @VValue prev_value, @VDt prev_dt, @VDt := dt dt, @VName := `name` `name`, @VValue := `value` `value` FROM example;

在我第一次发布时搞砸了这一点,请注意必须在从上一行返回变量后设置变量。要重新排序列(如果需要),您可以将此查询包装在另一个中,然后重新排序结果列。

如果您还有其他需要,请告诉我,

此致

詹姆斯

答案 1 :(得分:1)

这样的内容将返回“之前”的ID。行。

 SELECT x.*
      , MAX(y.id) prev_id
   FROM example x
   LEFT
   JOIN example y
     ON y.id < x.id 
    AND y.b = 1
  GROUP
     BY x.id;

我将离开与此行相关的其余数据作为读者练习的业务。