webentwicklung-frage-antwort-db.com.de

Wie wähle ich die erste Zeile für jede Gruppe in MySQL aus?

In C # würde es so aussehen:

table
   .GroupBy(row => row.SomeColumn)
   .Select(group => group
       .OrderBy(row => row.AnotherColumn)
       .First()
   )

Linq-To-Sql übersetzt es in den folgenden T-SQL-Code:

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]

Aber es ist nicht kompatibel mit MySQL.

55
Jader Dias

Wenn ich schreibe

SELECT AnotherColumn
FROM Table
GROUP BY SomeColumn
;

Es klappt. Eine solche IIRC-Anweisung ist in anderen RDBMS nicht möglich, da auf eine Spalte verwiesen wird, die nicht zum Gruppierungsschlüssel gehört, ohne irgendeine Art von Aggregation.

Diese "Eigenart" verhält sich sehr genau so, wie ich es will. Also habe ich es benutzt, um das gewünschte Ergebnis zu erzielen:

SELECT * FROM 
(
 SELECT * FROM `table`
 ORDER BY AnotherColumn
) t1
GROUP BY SomeColumn
;
20
Jader Dias

Ich habe meine Antwort nur auf den Titel Ihres Beitrags gestützt, da ich C # nicht kenne und die angegebene Abfrage nicht verstehe. Aber in MySQL empfehle ich Ihnen, Unterauswahlen zu versuchen. Holen Sie sich zuerst eine Reihe von Primärschlüsseln mit interessanten Spalten und wählen Sie dann Daten aus diesen Zeilen aus:

SELECT somecolumn, anothercolumn 
  FROM sometable 
 WHERE id IN (
               SELECT min(id) 
                 FROM sometable 
                GROUP BY somecolumn
             );
69
lfagundes

Hier ist eine andere Möglichkeit, die Sie ausprobieren können, da dieses ID-Feld nicht benötigt wird.

select some_column, min(another_column)
  from i_have_a_table
 group by some_column

Trotzdem stimme ich dem lfagundes zu, dass Sie einen Primärschlüssel hinzufügen sollten.

Beachten Sie auch, dass Sie auf diese Weise (leicht) nicht an die anderen Werte gelangen können, da dies dieselbe Zeile ist wie das resultierende some_colum, another_column-Paar! Dazu braucht man einen Bundeslehrling und einen PK!

15
lexu

Sie sollten eine Aggregatfunktion verwenden, um den gewünschten Wert für AnotherColumn zu erhalten. Das heißt, wenn Sie den niedrigsten Wert von AnotherColumn für jeden Wert von SomeColumn (entweder numerisch oder lexikografisch) möchten, können Sie Folgendes verwenden:

SELECT SomeColumn, MIN(AnotherColumn)
FROM YourTable
GROUP BY SomeColumn

Einige hoffentlich hilfreiche Links:

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

6
David M

From MySQL 5.7-Dokumentation

MySQL 5.7.5 und höher implementiert die Erkennung der Funktionsabhängigkeit. Wenn der SQL-Modus ONLY_FULL_GROUP_BY aktiviert ist (was standardmäßig der Fall ist), lehnt MySQL Abfragen ab, bei denen die Auswahlliste, die HAVING-Bedingung oder die ORDER BY-Liste auf nicht aggregierte Spalten verweisen, die in der GROUP BY-Klausel weder benannt sind noch funktional von diesen abhängig sind .

Dies bedeutet, dass die Lösung von @Jader Dias nicht überall funktioniert.

Hier ist eine Lösung, die funktionieren würde, wenn ONLY_FULL_GROUP_BY aktiviert:

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;
4
Nicolai

Ich habe die folgende Lösung unter den Antworten nicht gesehen, also dachte ich, ich würde sie dort veröffentlichen.

Das Problem besteht darin, Zeilen auszuwählen, die die ersten Zeilen sind, wenn sie nach AnotherColumn in allen Gruppen sortiert sind, die nach SomeColumn gruppiert sind.

Die folgende Lösung erledigt dies in MySQL. id muss eine eindeutige Spalte sein, die keine Werte enthalten darf, die - enthalten (die ich als Trennzeichen verwende).

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)

Es gibt eine Feature-Anfrage für FIRST() und LAST() im MySQL-Bug-Tracker, die jedoch vor vielen Jahren geschlossen wurde.

2
Lars Nyström

Eine weitere Möglichkeit (ohne Primärschlüssel) wäre die Verwendung der JSON-Funktionen:

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") )
  from sometable group by somecolumn

oder vor 5.7.22

select somecolumn, 
  json_unquote( 
    json_extract( 
      concat('["', group_concat(othercolumn separator '","') ,'"]') 
    ,"$[0]" ) 
  ) 
  from sometable group by somecolumn

Die Bestellung (oder Filterung) kann vor der Gruppierung erfolgen:

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) 
  from (select * from sometable order by othercolumn) as t group by somecolumn

... oder nach dem Gruppieren (natürlich):

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) as other 
  from sometable group by somecolumn order by other

Zugegeben, es ist ziemlich kompliziert und die Leistung ist wahrscheinlich nicht großartig (habe es nicht mit großen Datenmengen getestet, funktioniert gut mit meinen begrenzten Datenmengen).

1
Iikka
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;

Mit MySQL v8 + können Sie Fensterfunktionen verwenden

1

Noch ein anderer Weg, es zu tun

Wählen Sie aus der Gruppe, die in Ansichten funktioniert, die maximale Anzahl aus

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"
0
Timo Huovinen

Wie wäre es damit:

SELECT SUBSTRING_INDEX(
      MIN(CONCAT(OrderColumn, '|', IFNULL(TargetColumn, ''))
    ), '|', -1) as TargetColumn
FROM table
GROUP BY GroupColumn
0
Yura Fedoriv