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