Es gibt eine Tabelle messages
, die folgende Daten enthält:
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
Wenn ich eine Abfrage select * from messages group by name
starte, erhalte ich das Ergebnis als:
1 A A_data_1
4 B B_data_1
6 C C_data_1
Welche Abfrage liefert das folgende Ergebnis?
3 A A_data_3
5 B B_data_2
6 C C_data_1
Das heißt, der letzte Datensatz in jeder Gruppe sollte zurückgegeben werden.
Zur Zeit ist dies die Abfrage, die ich verwende:
SELECT
*
FROM (SELECT
*
FROM messages
ORDER BY id DESC) AS x
GROUP BY name
Das sieht jedoch sehr ineffizient aus. Gibt es noch andere Möglichkeiten, um dasselbe Ergebnis zu erzielen?
MySQL 8.0 unterstützt jetzt Fensterfunktionen wie fast alle gängigen SQL-Implementierungen. Mit dieser Standardsyntax können wir größte Abfragen pro Gruppe schreiben:
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;
Hier ist die ursprüngliche Antwort, die ich 2009 für diese Frage geschrieben habe:
Ich schreibe die Lösung so:
SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;
In Bezug auf die Leistung kann je nach Art Ihrer Daten die eine oder andere Lösung besser sein. Sie sollten also beide Abfragen testen und diejenige verwenden, die die Leistung Ihrer Datenbank verbessert.
Zum Beispiel habe ich eine Kopie des StackOverflow August-Datenabzugs . Ich werde das zum Benchmarking verwenden. Es gibt 1.114.357 Zeilen in der Tabelle Posts
. Dies läuft unter MySQL 5.0.75 auf meinem Macbook Pro 2.40GHz.
Ich werde eine Abfrage schreiben, um den neuesten Beitrag für eine bestimmte Benutzer-ID (meine) zu finden.
Zuerst mit der Technik gezeigt von @Eric mit dem GROUP BY
in einer Unterabfrage:
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)
Sogar die EXPLAIN
-Analyse dauert über 16 Sekunden:
+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| 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)
Erzeugen Sie jetzt dasselbe Abfrageergebnis mit my technique mit 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)
Die EXPLAIN
-Analyse zeigt, dass beide Tabellen ihre Indizes verwenden können:
+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 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)
Hier ist die DDL für meine Posts
Tabelle:
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, Version 5.7.5 von MySQL hat den Schalter ONLY_FULL_GROUP_BY standardmäßig aktiviert (daher wurden nicht deterministische GROUP BY-Abfragen deaktiviert). Außerdem wurde die GROUP BY-Implementierung aktualisiert und die Lösung funktioniert möglicherweise nicht mehr wie erwartet, selbst wenn der Schalter deaktiviert ist. Man muss das überprüfen.
Die obige Lösung von Bill Karwin funktioniert gut, wenn die Anzahl der Elemente in Gruppen eher gering ist, die Leistung der Abfrage jedoch schlecht wird, wenn die Gruppen ziemlich groß sind, da für die Lösung nur n*n/2 + n/2
von nur IS NULL
-Vergleichen erforderlich ist.
Ich habe meine Tests mit einer InnoDB-Tabelle von 18684446
-Zeilen mit 1182
-Gruppen durchgeführt. Die Tabelle enthält Testergebnisse für Funktionstests und hat den (test_id, request_id)
als Primärschlüssel. Daher ist test_id
eine Gruppe, und ich habe nach dem letzten request_id
für jeden test_id
gesucht.
Bills Lösung läuft bereits seit mehreren Stunden auf meinem Dell e4310, und ich weiß nicht, wann er beendet wird, obwohl er mit einem Abdeckungsindex arbeitet (daher using index
in EXPLAIN).
Ich habe einige andere Lösungen, die auf den gleichen Ideen basieren:
(group_id, item_value)
-Paar der letzte Wert in jedem group_id
. Dies ist der erste Wert für jeden group_id
, wenn wir den Index in absteigender Reihenfolge durchlaufen.3 Möglichkeiten, wie MySQL Indizes verwendet ist ein großartiger Artikel, um einige Details zu verstehen.
Lösung 1
Dieser ist unglaublich schnell, es dauert ungefähr 0,8 Sekunden auf meinen 18M + Reihen:
SELECT test_id, MAX(request_id), request_id
FROM testresults
GROUP BY test_id DESC;
Wenn Sie die Reihenfolge in ASC ändern möchten, legen Sie sie in eine Unterabfrage, geben Sie nur die IDs zurück und verwenden Sie diese als Unterabfrage, um sich mit den restlichen Spalten zu verbinden:
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;
Dies dauert ungefähr 1,2 Sekunden für meine Daten.
Lösung 2
Hier ist eine weitere Lösung, die für meinen Tisch etwa 19 Sekunden dauert:
SELECT test_id, request_id
FROM testresults, (SELECT @group:=NULL) as init
WHERE IF(IFNULL(@group, -1)[email protected]:=test_id, 0, 1)
ORDER BY test_id DESC, request_id DESC
Tests werden ebenfalls in absteigender Reihenfolge zurückgegeben. Es ist viel langsamer, da es einen vollständigen Index-Scan durchführt, aber hier ist eine Idee, wie N Zeilen für jede Gruppe ausgegeben werden.
Der Nachteil der Abfrage besteht darin, dass das Ergebnis nicht vom Abfrage-Cache zwischengespeichert werden kann.
Verwenden Sie Ihre Unterabfrage , um die korrekte Gruppierung zurückzugeben, da Sie sich auf halbem Weg befinden.
Versuche dies:
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
Wenn es nicht id
ist, möchten Sie das Maximum von:
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
Auf diese Weise vermeiden Sie korrelierte Unterabfragen und/oder Bestellungen in Ihren Unterabfragen, die in der Regel sehr langsam/ineffizient sind.
Ich bin zu einer anderen Lösung gekommen, nämlich die IDs für den letzten Beitrag in jeder Gruppe abzurufen und dann aus der Nachrichtentabelle mit dem Ergebnis der ersten Abfrage als Argument für ein WHERE x IN
-Konstrukt auszuwählen:
SELECT id, name, other_columns
FROM messages
WHERE id IN (
SELECT MAX(id)
FROM messages
GROUP BY name
);
Ich weiß nicht, wie sich dies im Vergleich zu einigen anderen Lösungen verhält, aber es funktionierte spektakulär für meinen Tisch mit mehr als drei Millionen Zeilen. (4 Sekunden Ausführung mit 1200+ Ergebnissen)
Dies sollte sowohl auf MySQL als auch auf SQL Server funktionieren.
Lösung durch Unterabfrage Geige-Link
select * from messages where id in
(select max(id) from messages group by Name)
Lösung Durch Join-Bedingung Geige-Link
select m1.* from messages m1
left outer join messages m2
on ( m1.id<m2.id and m1.name=m2.name )
where m2.id is null
Grund für diesen Beitrag ist es, nur den Fiddle-Link anzugeben.
Ich habe noch nicht mit großer Datenbank getestet, aber ich denke, das könnte schneller sein als das Verbinden von Tabellen:
SELECT *, Max(Id) FROM messages GROUP BY Name
Eine andere Möglichkeit, um den letzten zugehörigen Datensatz abzurufen, verwenden Sie GROUP_CONCAT
mit order by und SUBSTRING_INDEX
, um einen Datensatz aus der Liste auszuwählen
SELECT
`Id`,
`Name`,
SUBSTRING_INDEX(
GROUP_CONCAT(
`Other_Columns`
ORDER BY `Id` DESC
SEPARATOR '||'
),
'||',
1
) Other_Columns
FROM
messages
GROUP BY `Name`
Bei der obigen Abfrage werden alle Other_Columns
, die sich in derselben Name
-Gruppe befinden, gruppiert, und mit ORDER BY id DESC
werden alle Other_Columns
in einer bestimmten Gruppe in absteigender Reihenfolge mit dem bereitgestellten Trennzeichen in meinem Fall verbunden Wenn Sie ||
verwenden, wird mit SUBSTRING_INDEX
über dieser Liste der erste ausgewählt
Ein Ansatz mit beträchtlicher Geschwindigkeit ist wie folgt.
SELECT *
FROM messages a
WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)
Ergebnis
Id Name Other_Columns
3 A A_data_3
5 B B_data_2
6 C C_data_1
Hier ist meine Lösung:
SELECT
DISTINCT NAME,
MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES
FROM MESSAGE;
Hier sind zwei Vorschläge. Erstens, wenn mysql ROW_NUMBER () unterstützt, ist es sehr einfach:
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;
Ich gehe davon aus, dass Sie mit "last" das letzte in Id-Reihenfolge meinen. Wenn nicht, ändern Sie die ORDER BY-Klausel des ROW_NUMBER () - Fensters entsprechend. Wenn ROW_NUMBER () nicht verfügbar ist, ist dies eine andere Lösung:
Zweitens, wenn dies nicht der Fall ist, ist dies oft eine gute Vorgehensweise:
SELECT
Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
SELECT * FROM messages as M2
WHERE M2.Name = messages.Name
AND M2.Id > messages.Id
)
Wählen Sie also Nachrichten aus, bei denen keine spätere ID-Nachricht mit demselben Namen vorhanden ist.
SELECT
column1,
column2
FROM
table_name
WHERE id IN
(SELECT
MAX(id)
FROM
table_name
GROUP BY column1)
ORDER BY column1 ;
Sie können auch von hier aus sehen.
http://sqlfiddle.com/#!9/ef42b/9
ERSTE LÖSUNG
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);
ZWEITE LÖSUNG
SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;
Natürlich gibt es viele verschiedene Möglichkeiten, um die gleichen Ergebnisse zu erzielen. Ihre Frage scheint zu sein, wie Sie die letzten Ergebnisse in jeder Gruppe in MySQL effizient erhalten. Wenn Sie mit großen Datenmengen arbeiten und davon ausgehen, dass Sie InnoDB selbst mit den neuesten MySQL-Versionen verwenden (z. B. 5.7.21 und 8.0.4-rc), kann dies möglicherweise nicht effizient sein.
Manchmal müssen wir dies mit Tabellen mit mehr als 60 Millionen Zeilen tun.
Für diese Beispiele verwende ich Daten mit nur etwa 1,5 Millionen Zeilen, in denen die Abfragen Ergebnisse für alle Gruppen in den Daten finden müssten. In unseren tatsächlichen Fällen müssten wir häufig Daten von etwa 2.000 Gruppen zurückgeben (was hypothetisch nicht erforderlich wäre, um einen großen Teil der Daten zu prüfen).
Ich werde die folgenden Tabellen verwenden:
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));
Die Temperaturtabelle ist mit etwa 1,5 Millionen zufälligen Datensätzen und mit 100 verschiedenen Gruppen belegt .. Die ausgewählte Gruppe wird mit diesen 100 Gruppen gefüllt (in unseren Fällen wären dies normalerweise weniger als 20% für alle Gruppen).
Da diese Daten zufällig sind, bedeutet dies, dass mehrere Zeilen dieselben aufgezeichneten Zeitmarken haben können. Wir möchten eine Liste aller ausgewählten Gruppen in der Reihenfolge der Gruppen-ID mit dem letzten aufgezeichneten Zeitstempel für jede Gruppe erhalten. Wenn dieselbe Gruppe mehr als eine übereinstimmende Zeile enthält, dann die letzte übereinstimmende ID dieser Zeilen.
Wenn MySQL hypothetisch über eine last () - Funktion verfügte, die Werte aus der letzten Zeile einer speziellen ORDER BY-Klausel zurückgab, könnten wir einfach Folgendes tun:
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;
in diesem Fall müssten nur einige 100 Zeilen untersucht werden, da keine normalen GROUP BY-Funktionen verwendet werden. Dies würde innerhalb von 0 Sekunden ausgeführt werden und wäre daher sehr effizient. Beachten Sie, dass normalerweise in MySQL eine ORDER BY-Klausel hinter der GROUP BY-Klausel angezeigt wird. Wenn es nach GROUP BY wäre, würde es die GRUPPEN bestellen. Wenn keine GROUP BY-Klausel vorhanden ist, sind die letzten Werte in allen zurückgegebenen Zeilen gleich.
MySQL hat dies jedoch nicht. Schauen wir uns also die verschiedenen Vorstellungen an und beweisen, dass keine davon effizient ist.
Beispiel 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
);
Dies untersuchte 3.009.254 Reihen und dauerte bei 5.7.21 ~ 0.859 Sekunden und bei 8.0.4-rc etwas länger
Beispiel 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;
Dies untersuchte 1.505.331 Reihen und dauerte bei 5.7.21 ~ 1.25 Sekunden und bei 8.0.4-rc etwas länger
Beispiel 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;
Dies untersuchte 3.009.685 Reihen und dauerte bei 5.7.21 ~ 1.95 Sekunden und bei 8.0.4-rc etwas länger
Beispiel 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
)
);
Dies untersuchte 6.137.810 Reihen und dauerte bei 5.7.21 ~ 2.2 Sekunden und bei 8.0.4-RZ etwas länger
Beispiel 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;
Dies überprüfte 6,017,808 Reihen und dauerte ~ 4,2 Sekunden bei 8.0.4-RZ
Beispiel 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;
Dies überprüfte 6,017,908 Reihen und dauerte ~ 17,5 Sekunden bei 8.0.4-RZ
Beispiel 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;
Das hier dauerte ewig, also musste ich es töten.
Versuche dies:
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 )
Wenn Sie die letzte Zeile für jede Name
wünschen, können Sie jeder Zeilengruppe eine Zeilennummer mit Name
und absteigend nach Id
zuweisen.
ABFRAGE
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;
Gibt es eine Möglichkeit, diese Methode zum Löschen von Duplikaten in einer Tabelle zu verwenden? Die Ergebnismenge ist im Wesentlichen eine Sammlung eindeutiger Datensätze. Wenn wir also alle Datensätze löschen könnten, die nicht in der Ergebnismenge enthalten sind, hätten wir praktisch keine Duplikate. Ich habe dies versucht, aber mySQL gab einen Fehler 1093 aus.
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)
Gibt es eine Möglichkeit, die Ausgabe in einer temporären Variablen zu speichern und dann von NOT IN (temporäre Variable) zu löschen? @Bill danke für eine sehr nützliche Lösung.
EDIT: Ich glaube, ich habe die Lösung gefunden:
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);
Die unten stehende Abfrage funktioniert entsprechend Ihrer Frage.
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;
Hallo @Vijay Dev, wenn Ihre Tabelle messages enthält Id die automatisch den Primärschlüssel erhöht, die letzte Datensatzbasis für den Primärschlüssel abgerufen wird, die Ihre Abfrage wie folgt lautet:
SELECT m1.* FROM messages m1 INNER JOIN (SELECT max(Id) as lastmsgId FROM messages GROUP BY Name) m2 ON m1.Id=m2.lastmsgId
Wenn Sie wirklich Wert auf Leistung legen, können Sie in der Tabelle eine neue Spalte mit dem Namen IsLastInGroup
vom Typ BIT einfügen.
Setzen Sie ihn für die letzten Spalten auf "true" und pflegen Sie ihn bei jeder Zeile, die eingefügt/aktualisiert/gelöscht wird. Schreibvorgänge werden langsamer sein, aber bei Lesevorgängen profitieren Sie davon. Das hängt von Ihrem Anwendungsfall ab und ich empfehle es nur, wenn Sie sich auf das Lesen konzentrieren.
Ihre Anfrage sieht also so aus:
SELECT * FROM Messages WHERE IsLastInGroup = 1
wir werden uns ansehen, wie Sie mit MySQL den letzten Datensatz in einer Gruppe von Datensätzen abrufen können. Zum Beispiel, wenn Sie diese Ergebnismenge von Posts haben.
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
Ich möchte in der Lage sein, den letzten Beitrag in jeder Kategorie zu erhalten, die Titel 3, Titel 5 und Titel 6 sind. Um die Beiträge nach Kategorie zu erhalten, verwenden Sie die MySQL Group By-Tastatur.
select * from posts group by category_id
Aber die Ergebnisse, die wir aus dieser Abfrage erhalten, sind.
id category_id post_title
1 1 Title 1
4 2 Title 4
6 3 Title 6
Die Gruppe nach gibt immer den ersten Datensatz in der Gruppe in der Ergebnismenge zurück.
SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id );
Dadurch werden die Posts mit den höchsten IDs in jeder Gruppe zurückgegeben.
id category_id post_title
3 1 Title 3
5 2 Title 5
6 3 Title 6
Wie wäre es damit:
SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;
Ich hatte ein ähnliches Problem (auf postgresql tough) und auf einem 1M-Record-Tisch. Diese Lösung dauert 1,7s vs. 44s von der mit LEFT JOIN ..__ In meinem Fall musste ich den Korrespondenten Ihres name -Felds nach NULL-Werten filtern, was zu einer noch besseren Leistung um 0,2 Sekunden führte
select * from messages group by name desc
SELECT * FROM Tabellenname WHERE Primärschlüssel IN (SELECT MAX (Primärschlüssel) FROM Tabellenname GROUP BY Spaltenname)
Sie können durch Zählen gruppieren und erhalten auch das letzte Element der Gruppe wie:
SELECT
user,
COUNT(user) AS count,
MAX(id) as last
FROM request
GROUP BY user
Haben Sie https://github.com/fhulufhelo/Get-Last-Record-in-Each-MySQL-Group gesehen? Für mich geht das
$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";