MySQL difference between two rows of a SELECT Statement

后端 未结 5 1022
执笔经年
执笔经年 2020-11-27 20:53

I am trying to make the difference of two rows in an mysql database.
I have this table containing ID, kilometers, date, car_id, car_driver etc...
Since I don\'t alwa

相关标签:
5条回答
  • 2020-11-27 21:35
    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 Fiddle

    Or, by emulating a lag() function through 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 Fiddle

    0 讨论(0)
  • 2020-11-27 21:35

    With MySQL 8 you can use CTE and ROW_NUMBER window function to make a more readable query

    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;
    

    In this example I'm trying get the difference between datetime values.

    • The idea is to use ROW_NUMBER window function to assign an incremental number to each row after ordering by update_time.
    • The CTE allows us to write a subquery without having to repeat writing the same code.
    • We self join the CTE. The joining condition is basically - each nᵗʰ item of the second subquery joins with the n-1ᵗʰ item of the first subquery (this also means the first row will disappear from the result set. if you need it you can use a UNION to add the first row to the start).

    There are some good tutorials for: CTE (Common Table Expression), ROW_NUMBER and even window functions

    0 讨论(0)
  • 2020-11-27 21:54

    In Postgres, Oracle and SQL-Server 2012, this is plain simple, using the LAG() function:

    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 ;
    

    In MySQL, we have to do some nasty constructions. Either an inline subquery (with probably not very good performance):

    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 ;
    

    or a self-join (already provided by @Michael Fredrickson) or using MySQL variables (already provided as well).


    If you want the counter to start again from 0 for every car_id, which would be done with PARTITION BY in many other 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 ;
    

    it could be done in MySQL like this:

    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 ;
    
    0 讨论(0)
  • 2020-11-27 21:55

    Here's an example of using CURSOR for this use case as well

    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
    
    0 讨论(0)
  • 2020-11-27 21:58

    With data unsorted I can only think of inline subquery (not a good idea on the large table):

    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
    

    If you get data sorted you can use left join

    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
    

    You can probably tell that I'm more of a TSQL guy so you might need to adjust syntax for MySQL.

    0 讨论(0)
提交回复
热议问题