it-swarm.com.de

Den letzten Datensatz in jeder Gruppe abrufen - MySQL

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?

759
Vijay Dev

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;
796
Bill Karwin

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:

  • wenn der zugrunde liegende Index der BTREE-Index ist (was normalerweise der Fall ist), ist das größte (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.
  • wenn wir die Werte lesen, die von einem Index abgedeckt werden, werden die Werte in der Reihenfolge des Index gelesen.
  • jeder Index enthält implizit Primärschlüsselspalten, die an diesen angefügt sind (dh der Primärschlüssel befindet sich im Abdeckungsindex). In den folgenden Lösungen arbeite ich direkt mit dem Primärschlüssel. In Ihrem Fall müssen Sie lediglich Primärschlüsselspalten zum Ergebnis hinzufügen.
  • in vielen Fällen ist es viel billiger, die erforderlichen Zeilen-IDs in der erforderlichen Reihenfolge in einer Unterabfrage zu sammeln und das Ergebnis der Unterabfrage mit der ID zu verknüpfen. Da MySQL für jede Zeile in der Unterabfrage das Ergebnis eines einzelnen Abrufs auf der Grundlage des Primärschlüssels benötigt, wird die Unterabfrage an die erste Stelle des Joins gesetzt und die Zeilen werden in der Reihenfolge der IDs in der Unterabfrage ausgegeben (wenn wir explizites ORDER BY nicht angeben für den Join)

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.

135
newtover

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.

87
Eric

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.

39
JYelton

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.

26
Vipin

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
8
user942821

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 

Fiddle Demo

5
M Khalid Junaid

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
5
Song Zhengyi

Hier ist meine Lösung:

SELECT 
  DISTINCT NAME,
  MAX(MESSAGES) OVER(PARTITION BY NAME) MESSAGES 
FROM MESSAGE;
5
Abhishek Yadav

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.

5
Steve Kass
SELECT 
  column1,
  column2 
FROM
  table_name 
WHERE id IN 
  (SELECT 
    MAX(id) 
  FROM
    table_name 
  GROUP BY column1) 
ORDER BY column1 ;
4

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 ;
3
Shrikant Gupta

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.

3
Yoseph

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 )  
3
Pro Web Design

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;

SQL Fiddle

2
Ullas

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);
2
Simon

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;
2
Teja

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
2
bikashphp

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
1
jabko87

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

Referenz hier klicken

1
Yagnesh bhalala

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

1
Azathoth
select * from messages group by name desc
1
huuang

SELECT * FROM Tabellenname WHERE Primärschlüssel IN (SELECT MAX (Primärschlüssel) FROM Tabellenname GROUP BY Spaltenname)

0

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
0
Amir Forsati

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";
0