MySQL语句的两行之间存在差异

时间:2022-03-15 03:12:39

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 always enter the information in the table in the correct order, I may end up with information like this:

我试图在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

With a select statement I am able to sort my table correctly:

使用select语句,我能够正确排序我的表:

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

I will obtain this:

我会得到这个:

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

Now I would like to make a view where basically I have this extra information: Number of kilometers since last date and I would like to obtain something like this:

现在我想查看基本上我有这些额外信息的地方:自上次约会以来的公里数,我想得到这样的东西:

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

I thought of doing an INNER JOIN to perform what I wanted, but I have the feeling I can't do the join on my ID since they are not sorted correctly.
Is there a way to achieve what I want?

我想做一个INNER JOIN来执行我想要的东西,但我感觉我无法加入我的ID,因为它们没有正确排序。有没有办法实现我想要的?

Shall I create a view with a sort of row_number that I can then used in my INNER JOIN?

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

4 个解决方案

#1


16  

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...

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

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

#2


3  

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

在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 ;

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

在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 ;

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

或者自我加入(已由@Michael Fredrickson提供)或使用MySQL变量(已经提供)。


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:

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

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:

它可以在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 ;

#3


0  

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.

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

#4


0  

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

以下是使用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

#1


16  

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...

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

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

#2


3  

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

在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 ;

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

在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 ;

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

或者自我加入(已由@Michael Fredrickson提供)或使用MySQL变量(已经提供)。


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:

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

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:

它可以在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 ;

#3


0  

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.

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

#4


0  

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

以下是使用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