13

我正在尝试区分 mysql 数据库中的两行。
我有这张表,其中包含 ID、公里、日期、car_id、car_driver 等...
由于我并不总是以正确的顺序在表中输入信息,我最终可能会得到如下信息:

ID | Kilometers | date | car_id | car_driver | ...
 1 | 100        | 2012-05-04 | 1 | 1  
 2 | 200        | 2012-05-08 | 1 | 1
 3 | 1000       | 2012-05-25 | 1 | 1 
 4 | 600        | 2012-05-16 | 1 | 1

使用 select 语句,我可以正确地对表格进行排序:

SELECT * FROM mytable ORDER BY car_driver ASC, car_id ASC, date ASC

我会得到这个:

ID | Kilometers | date  | car_id | car_driver | ...  
 1 | 100        | 2012-05-04 | 1 | 1  
 2 | 200        | 2012-05-08 | 1 | 1
 4 | 600        | 2012-05-16 | 1 | 1  
 3 | 1000       | 2012-05-25 | 1 | 1

现在我想查看基本上我有这些额外信息的视图:自上次日期以来的公里数,我想获得这样的信息:

ID | Kilometers | date       | car_id | car_driver | number_km_since_last_date   
 1 | 100        | 2012-05-04 | 1 | 1 | 0  
 2 | 200        | 2012-05-08 | 1 | 1 | 100  
 4 | 600        | 2012-05-16 | 1 | 1 | 400  
 3 | 1000       | 2012-05-25 | 1 | 1 | 400

我想过做一个 INNER JOIN 来执行我想要的,但我觉得我不能在我的 ID 上做连接,因为它们没有正确排序。
有没有办法实现我想要的?

我应该创建一个带有某种 row_number 的视图,然后我可以在我的 INNER JOIN 中使用它吗?

4

5 回答 5

26
SELECT
    mt1.ID,
    mt1.Kilometers,
    mt1.date,
    mt1.Kilometers - IFNULL(mt2.Kilometers, 0) AS number_km_since_last_date   
FROM
    myTable mt1
    LEFT JOIN myTable mt2
        ON mt2.Date = (
            SELECT MAX(Date)
            FROM myTable mt3
            WHERE mt3.Date < mt1.Date
        )
ORDER BY mt1.date

Sql 小提琴

或者,lag()通过 MySql hackiness 模拟一个函数......

SET @kilo=0;

SELECT
    mt1.ID,
    mt1.Kilometers - @kilo AS number_km_since_last_date,
    @kilo := mt1.Kilometers Kilometers,
    mt1.date
FROM myTable mt1
ORDER BY mt1.date

Sql 小提琴

于 2013-02-13T15:48:23.920 回答
4

在 Postgres、Oracle 和 SQL-Server 2012 中,这很简单,使用以下LAG()函数:

SELECT
    id, kilometers, date,
    kilometers 
    - COALESCE( LAG(kilometers) OVER (ORDER BY date ASC, car_driver ASC, id ASC)
              , kilometers) 
        AS number_km_since_last_date
FROM
    mytable ;

在 MySQL 中,我们必须做一些讨厌的构造。一个内联子查询(性能可能不是很好):

SELECT
    id, kilometers, date,
    kilometers - COALESCE(
            ( SELECT p.kilometers
              FROM mytable AS p
              WHERE ( p.date = m.date AND p.car_driver = m.car_driver
                                                     AND p.id < m.id
                   OR p.date = m.date AND p.car_driver < m.car_driver
                   OR p.date < m.date
                    )
              ORDER BY p.date DESC, p.car_driver DESC
                  LIMIT 1
            ), kilometers) 
        AS number_km_since_last_date
FROM
    mytable AS m ;

或自连接(已由@Michael Fredrickson 提供)或使用 MySQL 变量(也已提供)。


如果您希望计数器从 0 重新开始 every car_id,这将PARTITION BY在许多其他 DBMS 中完成:

SELECT
    id, kilometers, date,
    kilometers 
    - COALESCE( LAG(kilometers) OVER (PARTITION BY car_id 
                                      ORDER BY date ASC, car_driver ASC, id ASC)
              , kilometers) 
        AS number_km_since_last_date
FROM
    mytable ;

它可以像这样在 MySQL 中完成:

SELECT
    id, kilometers, date,
    kilometers - COALESCE(
            ( SELECT p.kilometers
              FROM mytable AS p
              WHERE p.car_id = m.car_id
                AND ( p.date = m.date AND p.car_driver = m.car_driver
                                                     AND p.id < m.id
                   OR p.date = m.date AND p.car_driver < m.car_driver 
                   OR p.date < m.date
                    )
              ORDER BY p.date DESC, p.car_driver DESC
                  LIMIT 1
            ), kilometers) 
        AS number_km_since_last_date
FROM
    mytable AS m ;
于 2013-02-13T15:50:10.160 回答
0

对于未排序的数据,我只能想到内联子查询(在大表上不是一个好主意):

select t1.*,
t1.Kilometers - (select top 1 kilometers from mytable t2 where t2.date < t1.date order by t2.date desc) as number_km_since_last_date
from mytable t1

如果您对数据进行排序,则可以使用左连接

select t1.*
t1.Kilometers - t2.Kilometers as number_km_since_last_date
from mytable t1
left join mytable t2
  on t1.id = t2.id + 1

你可能会说我更像是一个 TSQL 人,所以你可能需要调整 MySQL 的语法。

于 2013-02-13T16:01:48.720 回答
0

这是在此用例中使用 CURSOR 的示例

CREATE TABLE TEMP1
(
    MyDate DATETIME,
    MyQty INT
)

INSERT INTO TEMP1 VALUES ('01/08/17', 100)
INSERT INTO TEMP1 VALUES ('01/09/17', 120)
INSERT INTO TEMP1 VALUES ('01/10/17', 180)

DECLARE @LastDate DATETIME = NULL
DECLARE @LastQty INT = NULL
DECLARE @MyDate DATETIME = NULL
DECLARE @MyQty INT = NULL

DECLARE mycursor CURSOR FOR
SELECT MyDate, MyQty FROM TEMP1 ORDER BY MyDate
OPEN mycursor
FETCH NEXT FROM mycursor INTO @MyDate, @MyQty

WHILE @@FETCH_STATUS = 0  
BEGIN  

    SELECT @MyDate, @MyQty - @LastQty

    SET @LastDate = @MyDate
    SET @LastQty = @MyQty

FETCH NEXT FROM mycursor INTO @MyDate, @MyQty
END

CLOSE mycursor
DEALLOCATE mycursor
于 2017-02-23T00:41:28.170 回答
0

使用 MySQL 8,您可以使用 CTE 和 ROW_NUMBER 窗口函数来进行更具可读性的查询

WITH cte_name AS (
    SELECT
    ROW_NUMBER() OVER (ORDER BY update_time) as row_num,
    id,
    other_data,
    update_time
    FROM table_name WHERE condition = 'some_condition'
)
SELECT t2.id, t2.other_data, TIMEDIFF(t2.update_time, t1.update_time) AS time_taken
FROM
cte_name t1
JOIN cte_name t2 ON t1.row_num = t2.row_num-1
ORDER BY time_taken;

在此示例中,我尝试获取日期时间值之间的差异。

  • 这个想法是使用 ROW_NUMBER 窗口函数在按 update_time 排序后为每一行分配一个增量编号。
  • CTE 允许我们编写子查询,而不必重复编写相同的代码。
  • 我们自己加入 CTE。连接条件基本上是 - 第二个子查询的每个 nᵗʰ 项与第一个子查询的 n-1ᵗʰ 项连接(这也意味着第一行将从结果集中消失。如果需要,可以使用 UNION 添加第一行开始)。

有一些很好的教程: CTE(通用表表达式)ROW_NUMBER甚至窗口函数

于 2020-04-03T07:52:55.917 回答