以下に示すようなデータを含むテーブルmessages
があります。
Id Name Other_Columns
-------------------------
1 A A_data_1
2 A A_data_2
3 A A_data_3
4 B B_data_1
5 B B_data_2
6 C C_data_1
select * from messages group by name
クエリを実行すると、結果は次のようになります。
1 A A_data_1
4 B B_data_1
6 C C_data_1
どのようなクエリが次の結果を返しますか?
3 A A_data_3
5 B B_data_2
6 C C_data_1
つまり、各グループの最後のレコードが返されるはずです。
現在のところ、これは私が使用しているクエリです:
SELECT
*
FROM (SELECT
*
FROM messages
ORDER BY id DESC) AS x
GROUP BY name
しかし、これは非常に非効率的に見えます。同じ結果を達成するための他の方法はありますか?
MySQL 8.0は、ほとんどすべての一般的なSQL実装のように、ウィンドウ機能をサポートするようになりました。この標準的な構文を使えば、グループあたり最大n回のクエリを書くことができます。
WITH ranked_messages AS (
SELECT m.*, ROW_NUMBER() OVER (PARTITION BY name ORDER BY id DESC) AS rn
FROM messages AS m
)
SELECT * FROM ranked_messages WHERE rn = 1;
下記は私が2009年にこの質問に対して書いた最初の答えです:
私はこのように解決策を書きます。
SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;
パフォーマンスに関しては、データの性質に応じて、どちらかの解決策を選択してください。そのため、両方のクエリをテストし、データベースのパフォーマンスに優れるものを使用する必要があります。
たとえば、 StackOverflow Augustデータダンプ のコピーがあります。ベンチマークに使用します。 Posts
テーブルには1,114,357行あります。私のMacbook Pro 2.40GHzでは、これは MySQL 5.0.75で動作しています。
特定のユーザーIDの最新の投稿を見つけるためのクエリを作成します(私の)。
最初に@Ericによる 表示された というテクニックをサブクエリでGROUP BY
と共に使用します:
SELECT p1.postid
FROM Posts p1
INNER JOIN (SELECT pi.owneruserid, MAX(pi.postid) AS maxpostid
FROM Posts pi GROUP BY pi.owneruserid) p2
ON (p1.postid = p2.maxpostid)
WHERE p1.owneruserid = 20860;
1 row in set (1 min 17.89 sec)
EXPLAIN
analysis でさえ16秒以上かかります:
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 76756 | |
| 1 | PRIMARY | p1 | eq_ref | PRIMARY,PostId,OwnerUserId | PRIMARY | 8 | p2.maxpostid | 1 | Using where |
| 2 | DERIVED | pi | index | NULL | OwnerUserId | 8 | NULL | 1151268 | Using index |
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
3 rows in set (16.09 sec)
LEFT JOIN
と 私のテクニック を使用して同じクエリ結果を生成します。
SELECT p1.postid
FROM Posts p1 LEFT JOIN posts p2
ON (p1.owneruserid = p2.owneruserid AND p1.postid < p2.postid)
WHERE p2.postid IS NULL AND p1.owneruserid = 20860;
1 row in set (0.28 sec)
EXPLAIN
分析は、両方のテーブルがそれらのインデックスを使用できることを示しています。
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 1 | SIMPLE | p1 | ref | OwnerUserId | OwnerUserId | 8 | const | 1384 | Using index |
| 1 | SIMPLE | p2 | ref | PRIMARY,PostId,OwnerUserId | OwnerUserId | 8 | const | 1384 | Using where; Using index; Not exists |
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
2 rows in set (0.00 sec)
これが私のPosts
テーブルのDDLです。
CREATE TABLE `posts` (
`PostId` bigint(20) unsigned NOT NULL auto_increment,
`PostTypeId` bigint(20) unsigned NOT NULL,
`AcceptedAnswerId` bigint(20) unsigned default NULL,
`ParentId` bigint(20) unsigned default NULL,
`CreationDate` datetime NOT NULL,
`Score` int(11) NOT NULL default '0',
`ViewCount` int(11) NOT NULL default '0',
`Body` text NOT NULL,
`OwnerUserId` bigint(20) unsigned NOT NULL,
`OwnerDisplayName` varchar(40) default NULL,
`LastEditorUserId` bigint(20) unsigned default NULL,
`LastEditDate` datetime default NULL,
`LastActivityDate` datetime default NULL,
`Title` varchar(250) NOT NULL default '',
`Tags` varchar(150) NOT NULL default '',
`AnswerCount` int(11) NOT NULL default '0',
`CommentCount` int(11) NOT NULL default '0',
`FavoriteCount` int(11) NOT NULL default '0',
`ClosedDate` datetime default NULL,
PRIMARY KEY (`PostId`),
UNIQUE KEY `PostId` (`PostId`),
KEY `PostTypeId` (`PostTypeId`),
KEY `AcceptedAnswerId` (`AcceptedAnswerId`),
KEY `OwnerUserId` (`OwnerUserId`),
KEY `LastEditorUserId` (`LastEditorUserId`),
KEY `ParentId` (`ParentId`),
CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`PostTypeId`) REFERENCES `posttypes` (`PostTypeId`)
) ENGINE=InnoDB;
UPD:2017-03-31、MySQLのバージョン 5.7.5 は、デフォルトでONLY_FULL_GROUP_BYスイッチを有効にしました(したがって、非決定的なGROUP BYクエリは無効になりました)。さらに、GROUP BY実装を更新し、無効なスイッチを使用してもソリューションが期待どおりに機能しなくなる可能性があります。確認する必要があります。
上記のBill Karwinのソリューションは、グループ内のアイテム数が少なければうまく機能しますが、グループが大きすぎるとクエリのパフォーマンスが低下します。ソリューションにはn*n/2 + n/2
の比較がIS NULL
しか必要ないためです。
18684446
グループを持つ1182
行のInnoDBテーブルでテストを行いました。このテーブルには、機能テストのテスト結果が含まれ、(test_id, request_id)
が主キーとして含まれています。したがって、test_id
はグループであり、request_id
ごとに最後のtest_id
を検索していました。
ビルのソリューションはすでに私のDell e4310で数時間実行されており、カバレッジインデックスで動作しているにもかかわらず、いつ終了するのかわかりません(したがってEXPLAINのusing index
)。
同じアイデアに基づいた他のソリューションがいくつかあります。
(group_id, item_value)
ペアは各group_id
内の最後の値、つまり各group_id
の最初の値です降順のインデックス。MySQLがインデックスを使用する3つの方法 は、詳細を理解するのに最適な記事です。
ソリューション1
これは非常に高速で、18M行以上で約0.8秒かかります。
SELECT test_id, MAX(request_id), request_id
FROM testresults
GROUP BY test_id DESC;
順序をASCに変更する場合は、サブクエリに入れて、idのみを返し、それをサブクエリとして使用して残りの列に結合します。
SELECT test_id, request_id
FROM (
SELECT test_id, MAX(request_id), request_id
FROM testresults
GROUP BY test_id DESC) as ids
ORDER BY test_id;
これは私のデータで約1,2秒かかります。
ソリューション2
テーブルに約19秒かかる別のソリューションを次に示します。
SELECT test_id, request_id
FROM testresults, (SELECT @group:=NULL) as init
WHERE IF(IFNULL(@group, -1)=@group:=test_id, 0, 1)
ORDER BY test_id DESC, request_id DESC
同様に降順でテストを返します。完全なインデックススキャンを実行するため、処理速度は大幅に低下しますが、ここでは各グループのN max行を出力する方法を説明します。
クエリの短所は、クエリキャッシュによって結果をキャッシュできないことです。
途中にいるので、正しいグループ化を返すために、 サブクエリ を使用してください。
これを試して:
select
a.*
from
messages a
inner join
(select name, max(id) as maxid from messages group by name) as b on
a.id = b.maxid
id
ではない場合は、以下の最大値が必要です。
select
a.*
from
messages a
inner join
(select name, max(other_col) as other_col
from messages group by name) as b on
a.name = b.name
and a.other_col = b.other_col
このようにして、相関のあるサブクエリやサブクエリ内の順序付け(非常に遅い/非効率的な傾向がある)を避けます。
それぞれのグループ内の最後の投稿のIDを取得し、最初のクエリの結果をWHERE x IN
コンストラクトの引数として使用して、messagesテーブルから選択するという別の解決策を見つけました。
SELECT id, name, other_columns
FROM messages
WHERE id IN (
SELECT MAX(id)
FROM messages
GROUP BY name
);
私がこれが他のソリューションのいくつかと比較してどのように機能するかはわかりませんが、300万行以上の私のテーブルでは壮観に機能しました。 (1200+の結果で4秒間の実行)
これはMySQLとSQL Serverの両方で動作するはずです。
私はまだ大きなDBでテストしていませんが、これはテーブルを結合するよりも速いかもしれないと思います:
SELECT *, Max(Id) FROM messages GROUP BY Name
これが私の解決策です:
SELECT
DISTINCT NAME,
MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES
FROM MESSAGE;
GROUP_CONCAT
にorder byおよびSUBSTRING_INDEX
を使用して最後の関連レコードを取得し、リストから1つのレコードを選択する別の方法を次に示します。
SELECT
`Id`,
`Name`,
SUBSTRING_INDEX(
GROUP_CONCAT(
`Other_Columns`
ORDER BY `Id` DESC
SEPARATOR '||'
),
'||',
1
) Other_Columns
FROM
messages
GROUP BY `Name`
上記のクエリは、同じName
グループ内にあるすべてのOther_Columns
をグループ化し、ORDER BY id DESC
を使用すると、特定のグループ内のすべてのOther_Columns
を指定された区切り記号で降順に結合します私の場合、このリストで||
を使用してSUBSTRING_INDEX
を使用した場合、最初のリストが選択されます
かなりのスピードでのアプローチは以下の通りです。
SELECT *
FROM messages a
WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)
結果
Id Name Other_Columns
3 A A_data_3
5 B B_data_2
6 C C_data_1
これは2つの提案です。まず、もしmysqlがROW_NUMBER()をサポートしていれば、それはとても簡単です。
WITH Ranked AS (
SELECT Id, Name, OtherColumns,
ROW_NUMBER() OVER (
PARTITION BY Name
ORDER BY Id DESC
) AS rk
FROM messages
)
SELECT Id, Name, OtherColumns
FROM messages
WHERE rk = 1;
私は "last"であなたがId順で最後を意味すると仮定します。そうでない場合は、それに応じてROW_NUMBER()ウィンドウのORDER BY句を変更します。 ROW_NUMBER()が利用できない場合、これは別の解決策です。
第二に、そうでない場合、これは先に進むための良い方法です。
SELECT
Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
SELECT * FROM messages as M2
WHERE M2.Name = messages.Name
AND M2.Id > messages.Id
)
つまり、同じ名前のlater-Idメッセージがないメッセージを選択します。
SELECT
column1,
column2
FROM
table_name
WHERE id IN
(SELECT
MAX(id)
FROM
table_name
GROUP BY column1)
ORDER BY column1 ;
こちらからも眺めることができます。
http://sqlfiddle.com/#!9/ef42b/9
最初の解決策
SELECT d1.ID,Name,City FROM Demo_User d1
INNER JOIN
(SELECT MAX(ID) AS ID FROM Demo_User GROUP By NAME) AS P ON (d1.ID=P.ID);
第2の解決策
SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;
明らかに同じ結果を得るためのたくさんの異なる方法がある、あなたの質問はMySQLの各グループで最後の結果を得るための効率的な方法が何であるかのようです。大量のデータを処理していて、InnoDBを最新バージョンのMySQL(5.7.21や8.0.4-rcなど)でも使用していると仮定している場合、これを行うのに効率的な方法はないかもしれません。
時にはこれを6000万行以上のテーブルで行う必要があります。
これらの例では、クエリでデータ内のすべてのグループの結果を見つける必要がある約150万行のみのデータを使用します。私たちの実際のケースでは、私たちは約2,000のグループからデータを返す必要があるでしょう(これは仮にデータの大部分を調べる必要はないでしょう)。
以下の表を使用します。
CREATE TABLE temperature(
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
groupID INT UNSIGNED NOT NULL,
recordedTimestamp TIMESTAMP NOT NULL,
recordedValue INT NOT NULL,
INDEX groupIndex(groupID, recordedTimestamp),
PRIMARY KEY (id)
);
CREATE TEMPORARY TABLE selected_group(id INT UNSIGNED NOT NULL, PRIMARY KEY(id));
温度テーブルには、約150万のランダムレコードと100の異なるグループがあります。 selected_groupには、100個のグループが設定されています(この例では、これは通常すべてのグループに対して20%未満です)。
このデータはランダムなので、複数の行が同じrecordingTimestampを持つことができます。欲しいのは、選択されたすべてのグループのリストを各グループの最後のrecordingTimestampと一緒にgroupIDの順序で取得し、同じグループにそのような一致する行が複数ある場合、それらの行の最後の一致するIDです。
仮にMySQLが特別なORDER BY句の最後の行から値を返すlast()関数を持っているならば、我々は単純にすることができます:
SELECT
last(t1.id) AS id,
t1.groupID,
last(t1.recordedTimestamp) AS recordedTimestamp,
last(t1.recordedValue) AS recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
ORDER BY t1.recordedTimestamp, t1.id
GROUP BY t1.groupID;
この場合、通常のGROUP BY関数を使用していないため、これは数100行を調べるだけで済みます。これは0秒で実行されるため、非常に効率的です。通常MySQLでは、GROUP BY句の後にORDER BY句が表示されますが、このORDER BY句はlast()関数のORDERを決定するために使用され、GROUP BYの後であればGROUPSを順序付けることになります。 GROUP BY句が存在しない場合、最後の値は返されるすべての行で同じになります。
しかしMySQLはこれを持っていないので、それが持っているもののさまざまなアイデアを見て、これらのどれもが効率的ではないことを証明しましょう。
例1
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
SELECT t2.id
FROM temperature t2
WHERE t2.groupID = g.id
ORDER BY t2.recordedTimestamp DESC, t2.id DESC
LIMIT 1
);
これは3,009,254行を調べ、5.7.21では約0.859秒、8.0.4-rcではやや長くなりました。
例2
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM temperature t1
INNER JOIN (
SELECT max(t2.id) AS id
FROM temperature t2
INNER JOIN (
SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
FROM selected_group g
INNER JOIN temperature t3 ON t3.groupID = g.id
GROUP BY t3.groupID
) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
GROUP BY t2.groupID
) t5 ON t5.id = t1.id;
これは1,505,331行を検査し、5.7.21では約1.25秒、8.0.4-rcではわずかに長くなりました。
例3
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM temperature t1
WHERE t1.id IN (
SELECT max(t2.id) AS id
FROM temperature t2
INNER JOIN (
SELECT t3.groupID, max(t3.recordedTimestamp) AS recordedTimestamp
FROM selected_group g
INNER JOIN temperature t3 ON t3.groupID = g.id
GROUP BY t3.groupID
) t4 ON t4.groupID = t2.groupID AND t4.recordedTimestamp = t2.recordedTimestamp
GROUP BY t2.groupID
)
ORDER BY t1.groupID;
これは3,009,685行を調べ、5.7.21では約1.95秒、8.0.4-rcではやや長くなりました。
例4
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.id = (
SELECT max(t2.id)
FROM temperature t2
WHERE t2.groupID = g.id AND t2.recordedTimestamp = (
SELECT max(t3.recordedTimestamp)
FROM temperature t3
WHERE t3.groupID = g.id
)
);
これは6,137,810行を調べ、5.7.21では最大2.2秒、8.0.4-rcではやや長かった
例5
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
SELECT
t2.id,
t2.groupID,
t2.recordedTimestamp,
t2.recordedValue,
row_number() OVER (
PARTITION BY t2.groupID ORDER BY t2.recordedTimestamp DESC, t2.id DESC
) AS rowNumber
FROM selected_group g
INNER JOIN temperature t2 ON t2.groupID = g.id
) t1 WHERE t1.rowNumber = 1;
これは6,017,808行を調べ、8.0.4-rcでは最大4.2秒かかりました
例6
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM (
SELECT
last_value(t2.id) OVER w AS id,
t2.groupID,
last_value(t2.recordedTimestamp) OVER w AS recordedTimestamp,
last_value(t2.recordedValue) OVER w AS recordedValue
FROM selected_group g
INNER JOIN temperature t2 ON t2.groupID = g.id
WINDOW w AS (
PARTITION BY t2.groupID
ORDER BY t2.recordedTimestamp, t2.id
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)
) t1
GROUP BY t1.groupID;
これは6,017,908行を調べ、8.0.4-rcでは最大17.5秒かかりました
例7
SELECT t1.id, t1.groupID, t1.recordedTimestamp, t1.recordedValue
FROM selected_group g
INNER JOIN temperature t1 ON t1.groupID = g.id
LEFT JOIN temperature t2
ON t2.groupID = g.id
AND (
t2.recordedTimestamp > t1.recordedTimestamp
OR (t2.recordedTimestamp = t1.recordedTimestamp AND t2.id > t1.id)
)
WHERE t2.id IS NULL
ORDER BY t1.groupID;
これは永遠に取っていたので、私はそれを殺さなければなりませんでした。
これを試して:
SELECT jos_categories.title AS name,
joined .catid,
joined .title,
joined .introtext
FROM jos_categories
INNER JOIN (SELECT *
FROM (SELECT `title`,
catid,
`created`,
introtext
FROM `jos_content`
WHERE `sectionid` = 6
ORDER BY `id` DESC) AS yes
GROUP BY `yes`.`catid` DESC
ORDER BY `yes`.`created` DESC) AS joined
ON( joined.catid = jos_categories.id )
こんにちは@Vijay Devあなたのテーブル messages が含まれている Id そしてあなたのクエリが以下のように読むべきである主キーに基づいて最新のレコードベースを取得するための自動増加主キーです:
SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId
各Name
の最後の行が必要な場合は、Name
で各行グループに行番号を付け、降順でId
で並べ替えることができます。
_ query _
SELECT t1.Id,
t1.Name,
t1.Other_Columns
FROM
(
SELECT Id,
Name,
Other_Columns,
(
CASE Name WHEN @curA
THEN @curRow := @curRow + 1
ELSE @curRow := 1 AND @curA := Name END
) + 1 AS rn
FROM messages t,
(SELECT @curRow := 0, @curA := '') r
ORDER BY Name,Id DESC
)t1
WHERE t1.rn = 1
ORDER BY t1.Id;
この方法を使用してテーブル内の重複を削除する方法はありますか。結果セットは基本的に一意のレコードの集まりなので、結果セットに含まれていないすべてのレコードを削除できれば、事実上重複することはありませんか。私はこれを試したが、mySQLは1093エラーを出した。
DELETE FROM messages WHERE id NOT IN
(SELECT m1.id
FROM messages m1 LEFT JOIN messages m2
ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL)
出力を一時変数に保存してからNOT IN(一時変数)から削除する方法はありますか?非常に便利な解決策をありがとうございます。
編集:私は解決策を見つけたと思います:
DROP TABLE IF EXISTS UniqueIDs;
CREATE Temporary table UniqueIDs (id Int(11));
INSERT INTO UniqueIDs
(SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON
(T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields
AND T1.ID < T2.ID)
WHERE T2.ID IS NULL);
DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);
以下のクエリはあなたの質問に従ってうまくいきます。
SELECT M1.*
FROM MESSAGES M1,
(
SELECT SUBSTR(Others_data,1,2),MAX(Others_data) AS Max_Others_data
FROM MESSAGES
GROUP BY 1
) M2
WHERE M1.Others_data = M2.Max_Others_data
ORDER BY Others_data;
パフォーマンスが本当にあなたの関心事であるならば、あなたはタイプBITのIsLastInGroup
と呼ばれるテーブルの上に新しい列を導入することができます。
最後の列でtrueに設定し、すべての行の挿入/更新/削除でそれを維持します。書き込みは遅くなりますが、読み取りにはメリットがあります。それはあなたのユースケースに依存します、そして、あなたが読み集中している場合だけ私はそれを推薦します。
そのため、クエリは次のようになります。
SELECT * FROM Messages WHERE IsLastInGroup = 1
group Byレコードの最後のレコードを取得する際にMySQLをどのように使用できるかを見ていきます。たとえば、投稿の結果セットがあるとします。
id category_id post_title
1 1 Title 1
2 1 Title 2
3 1 Title 3
4 2 Title 4
5 2 Title 5
6 3 Title 6
タイトル3、タイトル5、タイトル6の各カテゴリの最後の投稿を取得できるようにする必要があります。カテゴリ別に投稿するには、MySQL Group Byキーボードを使用します。
select * from posts group by category_id
しかし、このクエリから得られる結果は次のとおりです。
id category_id post_title
1 1 Title 1
4 2 Title 4
6 3 Title 6
Group byは常に結果セットのグループ内の最初のレコードを返します。
SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id );
これにより、各グループの最高のIDを持つ投稿が返されます。
id category_id post_title
3 1 Title 3
5 2 Title 5
6 3 Title 6
SELECT * FROM table_name WHERE primary_key IN(SELECT MAX(primary_key)FROM table_nameからGROUP BY column_name)
これはどう:
SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;
私は(postgresql厳しい)と1Mのレコードテーブルに同様の問題を抱えていた。この解決策は、LEFT JOINを使用した場合の1.7秒と44秒の間に発生します。私の場合、私はあなたの name フィールドの対応するものをNULL値に対してフィルタリングしなければなりませんでした。
select * from messages group by name desc
あなたは数えることによってグループ化することができて、そしてまたグループのようなグループの最後のアイテムを得ることができます:
SELECT
user,
COUNT(user) AS count,
MAX(id) as last
FROM request
GROUP BY user
https://github.com/fhulufhelo/Get-Last-Record-in-Each-MySQL-Group を見ましたか?わたしにはできる
$sql = "SELECT c.id, c.name, c.email, r.id, r.companyid, r.name, r.email FROM companytable c LEFT JOIN ( SELECT * FROM revisiontable WHERE id IN ( SELECT MAX(id) FROM revisiontable GROUP BY companyid )) r ON a.cid=b.r.id";