私はmysqlデータベースの2つの行の違いを作ろうとしています。
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で結合できない気がします。
私が望むことを達成する方法はありますか?
INNER JOINで使用できる一種のrow_numberを使用してビューを作成しますか?
_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
_
または、MySqlハックネスを介して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
_
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変数の使用(すでに提供されています)。
car_id
ごとにカウンターを0から再開する場合は、他の多くの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 ;
これは、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 ;
このユースケースにも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
ソートされていないデータでは、インラインサブクエリしか考えられません(大きなテーブルではお勧めできません)。
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の構文を調整する必要があるかもしれません。
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;
この例では、日時の値の差を取得しようとしています。
CTE(共通テーブル式) 、 ROW_NUMBER 、さらには ウィンドウ関数 の優れたチュートリアルがいくつかあります。