En C # ce serait comme ça:
table
.GroupBy(row => row.SomeColumn)
.Select(group => group
.OrderBy(row => row.AnotherColumn)
.First()
)
Linq-To-Sql le traduit en code T-SQL suivant:
SELECT [t3].[AnotherColumn], [t3].[SomeColumn]
FROM (
SELECT [t0].[SomeColumn]
FROM [Table] AS [t0]
GROUP BY [t0].[SomeColumn]
) AS [t1]
OUTER APPLY (
SELECT TOP (1) [t2].[AnotherColumn], [t2].[SomeColumn]
FROM [Table] AS [t2]
WHERE (([t1].[SomeColumn] IS NULL) AND ([t2].[SomeColumn] IS NULL))
OR (([t1].[SomeColumn] IS NOT NULL) AND ([t2].[SomeColumn] IS NOT NULL)
AND ([t1].[SomeColumn] = [t2].[SomeColumn]))
ORDER BY [t2].[AnotherColumn]
) AS [t3]
ORDER BY [t3].[AnotherColumn]
Mais c'est incompatible avec MySQL.
Quand j'écris
SELECT AnotherColumn
FROM Table
GROUP BY SomeColumn
;
Ça marche. IIRC dans un autre SGBDR, une telle déclaration est impossible, car une colonne qui n'appartient pas à la clé de regroupement est référencée sans aucune sorte d'agrégation.
Cette "bizarrerie" se comporte de très près à ce que je veux. Je l'ai donc utilisé pour obtenir le résultat souhaité:
SELECT * FROM
(
SELECT * FROM `table`
ORDER BY AnotherColumn
) t1
GROUP BY SomeColumn
;
J'ai basé ma réponse sur le titre de votre message uniquement, car je ne connais pas C # et je n'ai pas compris la requête donnée. Mais dans MySQL, je vous suggère d’essayer les sous-sélections. Commencez par obtenir un ensemble de clés primaires de colonnes intéressantes, puis sélectionnez des données dans ces lignes:
SELECT somecolumn, anothercolumn
FROM sometable
WHERE id IN (
SELECT min(id)
FROM sometable
GROUP BY somecolumn
);
Voici une autre façon d'essayer, qui n'a pas besoin de ce champ ID.
select some_column, min(another_column)
from i_have_a_table
group by some_column
Pourtant, je suis d’accord avec lfagundes pour ajouter une clé primaire.
Notez également qu'en faisant cela, vous ne pouvez pas (facilement) accéder aux autres valeurs qui se trouvent sur la même ligne que la paire some_colum, another_column résultante! Il faudrait que lfagundes apprach et un PK pour le faire!
Vous devez utiliser une fonction d'agrégat pour obtenir la valeur de AnotherColumn souhaitée. Autrement dit, si vous voulez la valeur la plus basse de AnotherColumn pour chaque valeur de SomeColumn (numériquement ou lexicographiquement), vous pouvez utiliser:
SELECT SomeColumn, MIN(AnotherColumn)
FROM YourTable
GROUP BY SomeColumn
Quelques liens utiles:
http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html
http://www.oreillynet.com/databases/blog/2007/05/debunking_group_by_myths.html
À partir de documentation MySQL 5.7
MySQL 5.7.5 et supérieur implémente la détection de la dépendance fonctionnelle. Si le mode SQL ONLY_FULL_GROUP_BY est activé (ce qu'il est par défaut), MySQL rejette les requêtes pour lesquelles la liste de sélection, la condition HAVING ou la liste ORDER BY font référence à des colonnes non agrégées qui ne sont ni nommées dans la clause GROUP BY ni fonctionnellement dépendantes. .
Cela signifie que la solution de @Jader Dias ne fonctionnerait pas partout.
Voici une solution qui fonctionnerait quand ONLY_FULL_GROUP_BY
est autorisé:
SET @row := NULL;
SELECT
SomeColumn,
AnotherColumn
FROM (
SELECT
CASE @id <=> SomeColumn AND @row IS NOT NULL
WHEN TRUE THEN @row := @row+1
ELSE @row := 0
END AS rownum,
@id := SomeColumn AS SomeColumn,
AnotherColumn
FROM
SomeTable
ORDER BY
SomeColumn, -AnotherColumn DESC
) _values
WHERE rownum = 0
ORDER BY SomeColumn;
Parmi les réponses, je n’ai pas vu la solution suivante, alors j’ai pensé la mettre en avant.
Le problème est de sélectionner les lignes qui sont les premières lignes quand elles sont classées par AnotherColumn
dans tous les groupes groupés par SomeColumn
.
La solution suivante le fera dans MySQL. id
doit être une colonne unique qui ne doit pas contenir de valeurs contenant -
(que j'utilise comme séparateur).
select t1.*
from mytable t1
inner join (
select SUBSTRING_INDEX(
GROUP_CONCAT(t3.id ORDER BY t3.AnotherColumn DESC SEPARATOR '-'),
'-',
1
) as id
from mytable t3
group by t3.SomeColumn
) t2 on t2.id = t1.id
-- Where
SUBSTRING_INDEX(GROUP_CONCAT(id order by AnotherColumn desc separator '-'), '-', 1)
-- can be seen as:
FIRST(id order by AnotherColumn desc)
-- For completeness sake:
SUBSTRING_INDEX(GROUP_CONCAT(id order by AnotherColumn desc separator '-'), '-', -1)
-- would then be seen as:
LAST(id order by AnotherColumn desc)
Il y a un demande de fonctionnalité pour FIRST()
et LAST()
dans le suivi des bogues MySQL, mais il a été fermé il y a de nombreuses années.
Une autre façon de le faire (sans la clé primaire) serait d'utiliser les fonctions JSON:
select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") )
from sometable group by somecolumn
ou avant le 5.7.22
select somecolumn,
json_unquote(
json_extract(
concat('["', group_concat(othercolumn separator '","') ,'"]')
,"$[0]" )
)
from sometable group by somecolumn
La commande (ou le filtrage) peut être effectuée avant de regrouper:
select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") )
from (select * from sometable order by othercolumn) as t group by somecolumn
... ou après regroupement (bien sûr):
select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) as other
from sometable group by somecolumn order by other
Certes, il est plutôt compliqué et les performances ne sont probablement pas géniales (je ne l'ai pas testé sur des données volumineuses, cela fonctionne bien sur mes jeux de données limités).
SELECT
t1.*
FROM
table_name AS t1
LEFT JOIN table_name AS t2 ON (
t2.group_by_column = t1.group_by_column
-- group_by_column is the column you would use in the GROUP BY statement
AND
t2.order_by_column < t1.order_by_column
-- order_by_column is column you would use in the ORDER BY statement
-- usually is the autoincremented key column
)
WHERE
t2.group_by_column IS NULL;
Avec MySQL v8 +, vous pouvez utiliser des fonctions de fenêtre
Encore une autre façon de le faire
Sélectionnez max dans le groupe qui fonctionne dans les vues
SELECT * FROM action a
WHERE NOT EXISTS (
SELECT 1 FROM action a2
WHERE a2.user_id = a.user_id
AND a2.action_date > a.action_date
AND a2.action_type = a.action_type
)
AND a.action_type = "CF"
Que dis-tu de ça:
SELECT SUBSTRING_INDEX(
MIN(CONCAT(OrderColumn, '|', IFNULL(TargetColumn, ''))
), '|', -1) as TargetColumn
FROM table
GROUP BY GroupColumn