web-dev-qa-db-fra.com

Récupération du dernier enregistrement de chaque groupe - MySQL

Il existe une table messages qui contient les données comme indiqué ci-dessous:

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

Si je lance une requête select * from messages group by name, le résultat sera le suivant:

1    A       A_data_1
4    B       B_data_1
6    C       C_data_1

Quelle requête retournera le résultat suivant?

3    A       A_data_3
5    B       B_data_2
6    C       C_data_1

Autrement dit, le dernier enregistrement de chaque groupe doit être renvoyé.

À l’heure actuelle, c’est la requête que j’utilise:

SELECT
  *
FROM (SELECT
  *
FROM messages
ORDER BY id DESC) AS x
GROUP BY name

Mais cela semble très inefficace. Y a-t-il d'autres moyens d'atteindre le même résultat?

759
Vijay Dev

MySQL 8.0 supporte maintenant les fonctions de fenêtrage, comme presque toutes les implémentations SQL populaires. Avec cette syntaxe standard, nous pouvons écrire les requêtes les plus importantes:

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;

Vous trouverez ci-dessous la réponse originale que j'ai écrite pour cette question en 2009:


J'écris la solution de cette façon:

SELECT m1.*
FROM messages m1 LEFT JOIN messages m2
 ON (m1.name = m2.name AND m1.id < m2.id)
WHERE m2.id IS NULL;

En ce qui concerne les performances, une solution ou l'autre peut être meilleure, en fonction de la nature de vos données. Vous devez donc tester les deux requêtes et utiliser celle qui offre les meilleures performances compte tenu de votre base de données.

Par exemple, j'ai une copie du StackOverflow August data dump . Je vais l'utiliser pour l'analyse comparative. Il y a 1 114 357 lignes dans la table Posts. Cela fonctionne sur MySQL 5.0.75 sur mon Macbook Pro 2.40GHz.

Je vais écrire une requête pour trouver le message le plus récent pour un ID utilisateur donné (le mien).

D'abord en utilisant la technique montrée par @Eric avec le GROUP BY dans une sous-requête:

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)

Même l'analyse EXPLAIN prend plus de 16 secondes:

+----+-------------+------------+--------+----------------------------+-------------+---------+--------------+---------+-------------+
| 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)

Produisez maintenant le même résultat de requête en utilisant ma technique avec 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)

L'analyse EXPLAIN montre que les deux tables peuvent utiliser leurs index:

+----+-------------+-------+------+----------------------------+-------------+---------+-------+------+--------------------------------------+
| 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)

Voici le DDL pour ma table Posts:

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, la version 5.7.5 de MySQL a activé le commutateur ONLY_FULL_GROUP_BY activé par défaut (par conséquent, les requêtes GROUP BY non déterministes ont été désactivées). De plus, elles ont mis à jour l'implémentation GROUP BY. et la solution risque de ne plus fonctionner comme prévu, même avec le commutateur désactivé. Il faut vérifier.} _

La solution ci-dessus de Bill Karwin fonctionne bien lorsque le nombre d'éléments dans les groupes est plutôt petit, mais les performances de la requête deviennent mauvaises lorsque les groupes sont plutôt grands, car la solution requiert environ n*n/2 + n/2 de seulement IS NULL comparaisons.

J'ai fait mes tests sur une table InnoDB de 18684446 lignes avec des groupes 1182. La table contient les résultats de test pour les tests fonctionnels et a le (test_id, request_id) comme clé primaire. Ainsi, test_id est un groupe et je cherchais le dernier request_id pour chaque test_id.

La solution de Bill fonctionne déjà depuis plusieurs heures sur mon Dell e4310 et je ne sais pas quand elle va se terminer alors qu'elle fonctionne sur un indice de couverture (d'où using index dans EXPLAIN).

J'ai quelques autres solutions basées sur les mêmes idées:

  • si l'index sous-jacent est l'index BTREE (comme c'est généralement le cas), la plus grande paire (group_id, item_value) est la dernière valeur de chaque group_id, c'est-à-dire la première pour chaque group_id si nous parcourons l'index par ordre décroissant;
  • si nous lisons les valeurs qui sont couvertes par un index, les valeurs sont lues dans l'ordre de l'index;
  • chaque index contient implicitement des colonnes de clé primaire ajoutées à cela (c'est-à-dire que la clé primaire est dans l'index de couverture). Dans les solutions ci-dessous, j'opère directement sur la clé primaire. Dans votre cas, il vous suffira d'ajouter des colonnes de clé primaire dans le résultat.
  • dans de nombreux cas, il est beaucoup moins coûteux de collecter les identifiants de ligne requis dans l'ordre requis dans une sous-requête et de joindre le résultat de la sous-requête sur l'identifiant. Comme pour chaque ligne du résultat de la sous-requête, MySQL aura besoin d’une seule extraction basée sur la clé primaire, la sous-requête sera placée en premier dans la jointure et les lignes seront affichées dans l’ordre des identifiants de la sous-requête (si nous omettons explicitement ORDER BY pour la jointure)

3 façons dont MySQL utilise les index est un excellent article pour comprendre certains détails.

Solution 1

Celui-ci est incroyablement rapide, il faut environ 0,8 seconde sur mes 18M + lignes:

SELECT test_id, MAX(request_id), request_id
FROM testresults
GROUP BY test_id DESC;

Si vous souhaitez modifier l'ordre en ASC, placez-le dans une sous-requête, renvoyez uniquement les identifiants et utilisez-le comme sous-requête à associer au reste des colonnes:

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;

Celui-ci prend environ 1,2 secondes sur mes données.

Solution 2

Voici une autre solution qui prend environ 19 secondes pour ma table:

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

Il renvoie également les tests dans un ordre décroissant. Il est beaucoup plus lent puisqu'il effectue un balayage d'index complet, mais il est là pour vous donner une idée de la façon de générer un nombre maximal de lignes pour chaque groupe. 

L'inconvénient de la requête est que son résultat ne peut pas être mis en cache par le cache de la requête.

135
newtover

Utilisez votre sous-requête pour renvoyer le groupe correct, car vous êtes à mi-chemin.

Essaye ça:

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

Si ce n'est pas id vous voulez le maximum de:

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

Ainsi, vous évitez les sous-requêtes et/ou les ordres corrélés dans vos sous-requêtes, qui ont tendance à être très lents/inefficaces.

87
Eric

Je suis arrivé à une solution différente, qui consiste à obtenir les ID du dernier message de chaque groupe, puis à sélectionner dans la table des messages en utilisant le résultat de la première requête comme argument d'une construction WHERE x IN:

SELECT id, name, other_columns
FROM messages
WHERE id IN (
    SELECT MAX(id)
    FROM messages
    GROUP BY name
);

Je ne sais pas comment cela fonctionne par rapport à certaines des autres solutions, mais cela a fonctionné de façon spectaculaire pour ma table avec plus de 3 millions de lignes. (4 secondes d'exécution avec plus de 1200 résultats)

Cela devrait fonctionner à la fois sur MySQL et SQL Server.

39
JYelton

Solution par sous-requête fiddle Link

select * from messages where id in
(select max(id) from messages group by Name)

Solution Par condition de joint lien fiddle

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

La raison de ce message est de ne donner que le lien fiddle.

26
Vipin

Je n'ai pas encore testé avec de grandes bases de données, mais je pense que cela pourrait être plus rapide que de joindre des tables

SELECT *, Max(Id) FROM messages GROUP BY Name
8
user942821

Voici un autre moyen d'obtenir le dernier enregistrement associé à l'aide de GROUP_CONCAT avec order by et de SUBSTRING_INDEX pour choisir l'un des enregistrements dans la liste. 

SELECT 
  `Id`,
  `Name`,
  SUBSTRING_INDEX(
    GROUP_CONCAT(
      `Other_Columns` 
      ORDER BY `Id` DESC 
      SEPARATOR '||'
    ),
    '||',
    1
  ) Other_Columns 
FROM
  messages 
GROUP BY `Name` 

La requête ci-dessus regroupera tous les Other_Columns qui sont dans le même groupe Name et en utilisant ORDER BY id DESC joignera tous les Other_Columns dans un groupe spécifique dans un ordre décroissant avec le séparateur fourni dans mon cas j’ai utilisé ||, en utilisant SUBSTRING_INDEX sur cette liste un 

Fiddle Démo

5
M Khalid Junaid

Une approche très rapide est la suivante.

SELECT * 
FROM messages a
WHERE Id = (SELECT MAX(Id) FROM messages WHERE a.Name = Name)

Résultat

Id  Name    Other_Columns
3   A   A_data_3
5   B   B_data_2
6   C   C_data_1
5
Song Zhengyi

Voici ma solution:

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

Voici deux suggestions. Premièrement, si mysql supporte ROW_NUMBER (), c'est très simple:

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;

Je suppose que par "dernier" vous voulez dire dernier dans l'ordre de l'identifiant. Sinon, modifiez la clause ORDER BY de la fenêtre ROW_NUMBER () en conséquence. Si ROW_NUMBER () n'est pas disponible, voici une autre solution:

Deuxièmement, si ce n’est pas le cas, c’est souvent une bonne façon de procéder:

SELECT
  Id, Name, OtherColumns
FROM messages
WHERE NOT EXISTS (
  SELECT * FROM messages as M2
  WHERE M2.Name = messages.Name
  AND M2.Id > messages.Id
)

En d'autres termes, sélectionnez des messages pour lesquels il n'y a pas de message Id-later portant le même nom.

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
jeet singh parmar

Vous pouvez voir à partir d'ici aussi. 

http://sqlfiddle.com/#!9/ef42b/9

PREMIÈRE SOLUTION 

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

SECONDE SOLUTION

SELECT * FROM (SELECT * FROM Demo_User ORDER BY ID DESC) AS T GROUP BY NAME ;
3
Shrikant Gupta

Clairement, il y a beaucoup de façons différentes d'obtenir les mêmes résultats, votre question semble être de savoir quel est le moyen efficace d'obtenir les derniers résultats dans chaque groupe de MySQL. Si vous travaillez avec d'énormes quantités de données et supposez que vous utilisez InnoDB avec les versions les plus récentes de MySQL (telles que 5.7.21 et 8.0.4-rc), il se peut qu'il ne soit pas efficace.

Nous avons parfois besoin de le faire avec des tables contenant même plus de 60 millions de lignes.

Pour ces exemples, je vais utiliser des données avec seulement environ 1,5 million de lignes pour lesquelles les requêtes doivent trouver des résultats pour tous les groupes des données. Dans nos cas réels, nous aurions souvent besoin de renvoyer des données d'environ 2 000 groupes (ce qui, en théorie, n'exigerait pas un examen très approfondi des données).

Je vais utiliser les tableaux suivants:

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

La table de température contient environ 1,5 million d'enregistrements aléatoires et 100 groupes différents . Le groupe sélectionné est composé de ces 100 groupes (dans notre cas, cette valeur serait normalement inférieure à 20% pour tous les groupes).

Comme ces données sont aléatoires, cela signifie que plusieurs lignes peuvent avoir le même enregistrement timestamp. Ce que nous voulons, c'est obtenir une liste de tous les groupes sélectionnés par ordre de groupID avec le dernier horodatage enregistré pour chaque groupe. Si le même groupe a plus d'une ligne correspondante, alors le dernier identifiant correspondant de ces lignes.

Si hypothétiquement MySQL avait une fonction last () qui renvoyait les valeurs de la dernière ligne d'une clause ORDER BY spéciale, nous pourrions simplement faire: 

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;

dans ce cas, il n’aurait besoin que d’examiner quelques 100 lignes car il n’utilise aucune des fonctions GROUP BY habituelles. Ceci s'exécute en 0 secondes et est donc très efficace . Notez que normalement, dans MySQL, une clause ORDER BY suivrait la clause GROUP BY, mais cette clause ORDER BY est utilisée pour déterminer l'ORDER de la dernière fonction (), si c'était après GROUP BY, alors ce serait de commander les GROUPES. Si aucune clause GROUP BY n'est présente, les dernières valeurs seront les mêmes dans toutes les lignes renvoyées.

Cependant, MySQL n’a pas cela, aussi examinons différentes idées et prouvons qu’aucune d’entre elles n’est efficace.

Exemple 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
);

Cela a examiné 3 009 254 lignes et a pris environ 0,859 seconde le 5.7.21 et légèrement plus long sur 8.0.4-rc

Exemple 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;

Cela a examiné 1 505 331 lignes et a pris environ 1,25 seconde le 5.7.21 et légèrement plus long sur 8.0.4-rc

Exemple 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;

Cela a examiné 3 009 685 lignes et a pris environ 1,95 seconde le 5.7.21 et légèrement plus longtemps le 8.0.4-rc

Exemple 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
    )
);

Cela a examiné 6.137.810 lignes et a pris ~ 2,2 secondes sur 5.7.21 et légèrement plus long sur 8.0.4-rc

Exemple 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;

Cela a examiné 6 017 808 lignes et a pris environ 4,2 secondes sur 8.0.4-rc

Exemple 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;

Cela a examiné 6 017 908 lignes et a pris environ 17,5 secondes sur 8.0.4-rc

Exemple 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;

Celui-ci prenait une éternité, je devais donc le tuer.

3
Yoseph

Essaye ça:

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

Si vous voulez la dernière ligne pour chaque Name, vous pouvez attribuer un numéro de ligne à chaque groupe de lignes par la valeur Name et l'ordre par Id par ordre décroissant.

REQU&ECIRC;TE

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

Existe-t-il un moyen d'utiliser cette méthode pour supprimer les doublons dans un tableau? L'ensemble de résultats est essentiellement une collection d'enregistrements uniques. Par conséquent, si nous pouvions supprimer tous les enregistrements qui n'étaient pas dans l'ensemble de résultats, nous n'aurions en réalité aucun doublon. J'ai essayé cela, mais MySQL a donné une erreur 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)

Existe-t-il un moyen de sauvegarder la sortie dans une variable temp, puis de supprimer de NOT IN (variable temp)? @ Bill Merci pour une solution très utile.

EDIT: Pense que j'ai trouvé la solution:

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

La requête ci-dessous fonctionnera correctement selon votre question.

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

Bonjour @Vijay Dev si votre table messages contient Id qui est une clé primaire à incrémentation automatique, puis pour récupérer le dernier enregistrement sur la clé primaire que votre requête devrait lire comme suit:

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

Si la performance est vraiment votre préoccupation, vous pouvez introduire une nouvelle colonne sur la table appelée IsLastInGroup de type BIT. 

Définissez-le sur true sur les dernières colonnes et conservez-le à chaque ligne insérez/mettez à jour/supprimez. Les écritures seront plus lentes, mais vous bénéficierez des lectures. Cela dépend de votre cas d'utilisation et je ne le recommande que si vous vous concentrez sur la lecture.

Votre requête ressemblera donc à:

SELECT * FROM Messages WHERE IsLastInGroup = 1
1
jabko87

nous verrons comment utiliser MySQL pour obtenir le dernier enregistrement d'un groupe par enregistrements. Par exemple, si vous avez cet ensemble de résultats.

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

Je souhaite pouvoir obtenir le dernier message de chaque catégorie, titre 3, titre 5 et titre 6. Pour utiliser les messages classés par catégorie, vous utiliserez le groupe MySQL Group by keyboard.

select * from posts group by category_id

Mais les résultats obtenus avec cette requête sont.

id category_id post_title

1 1 Title 1

4 2 Title 4

6 3 Title 6

Le groupe de retournera toujours le premier enregistrement du groupe dans le jeu de résultats.

SELECT id, category_id, post_title FROM posts WHERE id IN ( SELECT MAX(id) FROM posts GROUP BY category_id );

Cela renverra les messages avec les ID les plus élevés dans chaque groupe.

id category_id post_title

3 1 Title 3

5 2 Title 5

6 3 Title 6

Référence Cliquez ici

1
Yagnesh bhalala

Que dis-tu de ça:

SELECT DISTINCT ON (name) *
FROM messages
ORDER BY name, id DESC;

J'ai eu un problème similaire (sur postgresql difficile) et sur une table d'enregistrements 1M. Cette solution nécessite 1.7s vs 44s produits par celui avec LEFT JOIN . Dans mon cas, je devais filtrer le corrispondant de votre champ name avec les valeurs NULL, ce qui donnait des performances encore meilleures de 0,2 s.

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

SELECT * FROM nom_table WHERE primaire_key IN (SELECT MAX (clé_primaire) FROM nom_table GROUP BY nom_colonne)

0
Shri Prakash Singh

Vous pouvez grouper en comptant et aussi obtenir le dernier élément du groupe comme:

SELECT 
    user,
    COUNT(user) AS count,
    MAX(id) as last
FROM request 
GROUP BY user
0
Amir Forsati

Avez vous vu https://github.com/fhulufhelo/Get-Last-Record-in-Each-MySQL-Group ? ça marche pour moi

$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