MYSQL - 最高の結果でグループ化して選択し、JOINを使用しますか?

私は2つのSQLテーブルを持っています、私は2番目のテーブルから最も高いタイムスタンプを引き出す必要があり、そのタイムスタンプは範囲内にある必要があります。

Table: Projects 
ID,Name,invoiceTotal,department
1,Project1,100,1
2,Project2,200,3
3,Project3,300,2
4,Project4,100,2
5,Project5,400,1


Table: Manning
ProjectID,timestamp
1,1547038287
1,1515558287
1,1501118287
5,1471118287
5,1481118287

私は、PHPを使用して最初に内部結合を持つクエリを実行し、両方のテーブルデータを取得できるようにしてきました。

SELECT
project.ID,
project.name,
project.invoiceTotal,
manning.timestamp
FROM
manning
INNER JOIN projects ON project.ID = manning.ProjectID
WHERE
project.department = 1 AND
manning.timestamp BETWEEN 1547038287 AND 1301118287
ORDER BY
manning.timestamp ASC

私はそれからPHPでループし、データをソートして表示するための手続き型コードを実行します。問題はそれが遅いことであり、そしてそれがそれをする最も効率的な方法ではないことは間違いない。

だから、この例のデータで私は結果が欲しいのですが:

1,Project1,100,1547038287
5,Project5,400,1481118287

私は「group by」を使おうとしていましたが、もしマニングテーブルの順番が正しくなければ、正しい結果が得られません。

助けてくれてありがとう。私はsqlfiddle.comを使用しようとしました、しかしそれは今私のためにダウンしているようです:(

1
nl ru de
manning.timestamp 1547038287 AND 1301118287 manning.timestamp> = 1547038287の省略形であり、1547038287は1301118287より大きいため、trueにはなりません。
追加された 著者 Thorsten Kettner,
最大で値を持つレコードを取得するSQL結果のグループごとに
追加された 著者 sticky bit,
@ThorstenKettnerは、サンプルクエリを無効にしている間の私の部分に対する見落としを指摘しました。
追加された 著者 Chris Kerrison,

4 答え

MySQL 8以降を使用している場合、これを処理するための良い方法は ROW_NUMBER()を使うことです。

WITH cte AS (
    SELECT p.ID, p.Name, p.invoiceTotal, m.timestamp,
        ROW_NUMBER() OVER (PARTITION BY m.ProjectID ORDER BY m.timestamp DESC) rn
    FROM Projects p
    INNER JOIN Manning m
        ON p.ID = m.ProjectID
    WHERE m.timestamp BETWEEN 1301118287 and 1547038287
)

SELECT ID, Name, invoiceTotal, timestamp
FROM cte
WHERE rn = 1;

enter image description here

Demo

8より前のバージョンのMySQLを使用していて、多くの種類のクエリに必要なコード量を減らすことができる場合は、アップグレードを検討してください。以前のバージョンのMySQLで問題に対処する方法については他の回答を確認してください。

0
追加された

すべての人員配置行に参加するのではなく、プロジェクトごとに最新の人員行だけに参加します。

select
  p.ID,
  p.name,
  p.invoiceTotal,
  m.max_timestamp
from projects p
join
(
  select projectid, max(timestamp) as max_timestamp
  from manning
  where timestamp between 1301118287 and 1547038287
  group by projectid
) m on m.projectid = p.id
where p.department = 1;

これはこのクエリを書くためのきれいな方法であるだけでなく、非常に高性能なアプローチでもあります。ごく少数のレコードに興味があるだけなのに、なぜすべてのレコードに参加するのですか。あなたが再び取り除かなければならない巨大な中間結果を生み出さないでください。

0
追加された
お返事をいただきありがとうございます。それは働いていて、それが以前よりずっと速いです:)
追加された 著者 Chris Kerrison,

試してください:

SELECT
    p.ID,
    p.name,
    p.invoiceTotal,
    maxm.ts
FROM
    project p
    INNER JOIN (
        SELECT m.projectID, max(m.timestamp) ts
        FROM manning m
        WHERE p.ID = m.ProjectID AND m.timestamp BETWEEN 1301118287 AND 1547038287
        GROUP BY m.projectID
    ) maxm ON maxm.projectID = p.id
ORDER BY m.timestamp ASC
0
追加された

WHERE句以外に、HAVING句を使用してGROUP BYクエリの結果をフィルタ処理できます。

0
追加された
しかし、これはここでどのように役立ちますか?
追加された 著者 Thorsten Kettner,