2016-07-19 7 views
1

Ich habe die folgende Tabelle (SQFiddle, with sample data here):MySql Rückkehr Zeilen mit dem höchsten Wert, am oder vor dem Datum

|-------------------------------------------------------| 
| id |data_date | value | score |  created_at  | 
|-------------------------------------------------------| 
| 1 | 2015-01-01 | 10 | 10 | 2016-07-01 09:00:00 | 
| 2 | 2015-02-01 | 10 | 10 | 2016-07-01 09:00:00 | 
| 3 | 2015-03-01 | 10 | 10 | 2016-07-01 09:00:00 | 
| 4 | 2015-01-01 | 15 | 20 | 2016-07-02 09:00:00 | 
| 5 | 2015-03-01 | 15 | 20 | 2016-07-02 09:00:00 | 
| 6 | 2015-03-01 | 15 | 15 | 2016-07-03 09:00:00 | 
|-------------------------------------------------------| 

Was ich tun möchte, zurückzukehren für jeden data_data einen einzelnen Datensatz ist, mit der höchsten Punktzahl auf einem gegebenen created_at Datum.

Das erwartete Ergebnis für die Ergebnisse auf 2016.07.02 wäre:

|-------------------------------------------------------| 
| id |data_date | value | score |  created_at  | 
|-------------------------------------------------------| 
| 4 | 2015-01-01 | 15 | 20 | 2016-07-02 09:00:00 | 
| 2 | 2015-02-01 | 10 | 10 | 2016-07-01 09:00:00 | 
| 5 | 2015-03-01 | 15 | 20 | 2016-07-02 09:00:00 | 
|-------------------------------------------------------| 

Was habe ich ist bisher erreichen den einzigen höchste erzielte Rekord für jeden data_data zurückzukehren, aber ich kann nicht scheinen, um die richtige Bedingung hinzuzufügen, um das Feld created_at zu berücksichtigen.

select `my_table`.* 
from `my_table` 
left outer join `my_table` as `t2` 
on `my_table`.`data_date` = `t2`.`data_date` AND 
(
    (`my_table`.`score` < `t2`.`score`) OR 
    (`my_table`.`score` = `t2`.`score` AND `my_table`.`id` < `t2`.`id`) 
) 
where `t2`.`data_date` is null 
order by `my_table`.`data_date` asc 

Dies liefert folgende Ergebnisse:

|-------------------------------------------------------| 
| id |data_date | value | score |  created_at  | 
|-------------------------------------------------------| 
| 4 | 2015-01-01 | 15 | 20 | 2016-07-02 09:00:00 | 
| 2 | 2015-02-01 | 10 | 10 | 2016-07-01 09:00:00 | 
| 6 | 2015-03-01 | 15 | 15 | 2016-07-03 09:00:00 | 
|-------------------------------------------------------| 

eine Abfrage wie die folgenden verwenden, alle Datensätze am oder vor dem 2. Juli mit der höchsten Punktzahl erstellt zu bekommen:

select `my_table`.* 
from `my_table` 
left outer join `my_table` as `t2` 
on `my_table`.`data_date` = `t2`.`data_date` AND 
(
    (`my_table`.`score` < `t2`.`score`) OR 
    (`my_table`.`score` = `t2`.`score` AND `my_table`.`id` < `t2`.`id`) 
) AND 
DATE_FORMAT(my_table.created_at, '%Y-%m-%d') <= '2016-07-02' AND 
DATE_FORMAT(t2.created_at, '%Y-%m-%d') <= '2016-07-02' 
where `t2`.`data_date` is null 
order by `my_table`.`data_date` asc 

Returns das folgende falsche Ergebnis (Zeile 6 sollte nicht zurückgegeben werden):

|-------------------------------------------------------| 
| id |data_date | value | score |  created_at  | 
|-------------------------------------------------------| 
| 4 | 2015-01-01 | 15 | 20 | 2016-07-02 09:00:00 | 
| 2 | 2015-02-01 | 10 | 10 | 2016-07-01 09:00:00 | 
| 5 | 2015-03-01 | 15 | 20 | 2016-07-02 09:00:00 | 
| 6 | 2015-03-01 | 15 | 15 | 2016-07-03 09:00:00 | 
|-------------------------------------------------------| 

Zusammengefasst

Für einen einzelnen data_date können mehrere Datensätze sein, die jeweils eine andere value und eine andere score haben. Ich möchte die einzelne Zeile für jede data_data zurückgeben, die die höchste score hat, die an oder vor einem bestimmten Datum erstellt wurde. I.e. Wenn ein data_date einen Datensatz an jedem Tag der Woche (Mo-Fr) erstellt hat, möchte ich vielleicht den höchsten Wert, der am Mittwoch für jeden verfügbar war data_data.

+0

Aus Ihrer Beschreibung, sicherlich die Ergebnisse für 07-01 sind 1,2,3 – Strawberry

+0

Sie haben recht. Ich hatte mich vertippt, in der obigen Frage behoben. – Amo

+0

OK, aber jetzt verstehe ich nicht, warum 2 überhaupt im Ergebnis erscheinen würde. – Strawberry

Antwort

1

Eine Geige, um so etwas zu tun, ist (ab) die Funktion GROUP_CONCAT zu verwenden. Sie können nach dem Feld gruppieren, nach dem Sie gruppieren möchten, und GROUP_CONCAT für jedes andere Feld verwenden, das absteigend nach dem Feld sortiert ist, für das Sie den Maximalwert haben möchten. Dadurch erhalten Sie alle Werte für jeden miteinander verketteten Wert.

Sie können dann SUBSTRING_INDEX verwenden, um nur den ersten Wert von jedem zu erhalten.

SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY score DESC), ',', 1), 
    data_date, 
    SUBSTRING_INDEX(GROUP_CONCAT(value ORDER BY score DESC), ',', 1), 
    SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score DESC), ',', 1), 
    SUBSTRING_INDEX(GROUP_CONCAT(created_at ORDER BY score DESC), ',', 1) 
FROM my_table 
GROUP BY data_date 

In diesem Beispiel ist es ziemlich einfach. Wenn Sie Textfelder haben, die ein Komma enthalten können, oder Sie NULL-Werte haben, wird es ein bisschen komplizierter.

Dies bringt Sie Ihre vorhandenen Ergebnisse möglicherweise effizienter.Aber ich bin nicht sicher, wie Sie versuchen, in dem created_at Datum Faktor, es sei denn, Sie meinen Sie nur unter Berücksichtigung aller Aufzeichnungen nehmen wollen von vor einem bestimmten created_at Datum: -

SELECT SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY score DESC), ',', 1), 
    data_date, 
    SUBSTRING_INDEX(GROUP_CONCAT(value ORDER BY score DESC), ',', 1), 
    SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score DESC), ',', 1), 
    SUBSTRING_INDEX(GROUP_CONCAT(created_at ORDER BY score DESC), ',', 1) 
FROM my_table 
WHERE created_at <= '2016-07-02 23:59:59' 
GROUP BY data_date 

EDIT

Diese Die zweite Abfrage ruft alle Datensätze vor dem Ende eines bestimmten Datums ab (Ich habe das Datum/die Zeit auf diese Weise verwendet, anstatt den Datumsteil aus dem Wert in der Spalte zu extrahieren, da dies einen Index für das Datum/die Zeit zulässt verwendet, während das Extrahieren des Datumsteils verhindert, dass ein Index verwendet wird, und erzwingt auch die Verwendung einer Funktion für jede Zeile in der Tabelle). Für alle übereinstimmenden Datensätze gruppiert sie diese nach dem Feld data_date. Für die Felder id, value, score und created_at verwendet es GROUP_CONCAT, um alle Werte für jedes data_date zu gruppieren, wobei jeder Wert durch ein Komma getrennt ist (der Standardwert), geordnet nach dem Ergebnis absteigend.

Auf seiner Bühne für die data_date von 2015.01.01 das id-Feld ‚4,1‘ enthält, den Wert enthält ‚15,10‘, wird Punktzahl enthalten ‚20,10‘ und created_at enthält '2016-07-02 09: 00: 00,2016-07-01 09:00:00'.

SUBSTRING_INDEX wird dann verwendet, um alles bis zum ersten Komma für jedes dieser Felder zu erhalten. Da sie sich in absteigender Reihenfolge befinden, erhalten sie den Wert für jede, die der höchsten Punktzahl entspricht.

+0

Für ein einzelnes '' 'data_date''' kann es mehrere Datensätze geben, die jeweils einen anderen' '' Wert '' 'und einen anderen' '' Wert''' haben. Ich möchte die einzelne Zeile für jedes '' data_data''' zurückgeben, welches die höchste '' 'score''' hat, die am oder vor einem bestimmten Datum erstellt wurde. I.e. Wenn ein '' 'data_date''' an jedem Wochentag einen Datensatz erstellt hat (mon-fri), möchte ich vielleicht den höchsten bewerteten Wert, der am Mittwoch verfügbar war. Ich hoffe das klärt meine Frage auf. – Amo

+0

@Amo - in diesem Fall denke ich, dass mein 2. SQL Beispiel ist, was Sie brauchen (obwohl nicht getestet - sql Geige stürzt 95% der Zeit für mich ab, wenn ich es versuche und verwende) – Kickstart

+0

Würde es Ihnen etwas ausmachen zu erklären, was die zweite Abfrage ist tun? – Amo

-1

folgende Abfrage wird in Ihrem Fall funktionieren

select max(id), data_date, max(value), max(score), max(created_at) 
from  
    my_table 
where score in (select max(score) from my_table group by data_date) 
group by data_date; 
+0

Das wird die Werte für jedes data_date geben, anstatt die Werte aus der Zeile mit der maximalen Punktzahl – Kickstart

+0

haben Sie diese Abfrage ausführen ??? –

1
SELECT x.* 
    FROM my_table x 
    JOIN 
    (SELECT a.data_date 
      , a.created_at 
      , MAX(a.score) score 
     FROM my_table a 
     JOIN 
      (SELECT data_date 
        , MAX(created_at) created_at 
       FROM my_table 
       WHERE created_at <= '2016-07-02 23:59:59' 
       GROUP 
        BY data_date 
      ) b 
      ON b.data_date = a.data_date 
      AND b.created_at = a.created_at 
     GROUP 
      BY a.data_date 
      , a.created_at 
    ) y 
    ON y.data_date = x.data_date 
    AND y.created_at = x.created_at 
    AND y.score = x.score; 
+0

Gibt es irgendwelche Vorteile bei der Verwendung dieser Abfrage gegenüber der, die @Kickstart gepostet hat, und können Sie erklären, was etwas mehr vor sich geht? – Amo

+0

konnte ich nicht sagen. Meine Abfrage ist Ihrer sehr ähnlich, außer dass sie die zusätzlichen Kriterien berücksichtigt. In der Regel wird eine uncorelated Unterabfrage die meisten anderen Möglichkeiten übertreffen, dasselbe zu erreichen. Außerdem ist es nicht an irgendwelche Einschränkungen gebunden, die mit GROUP_CONCAT verbunden sind, aber das ist kein Grund, BOTH-Lösungen nicht zu aktualisieren. – Strawberry

+0

Da ich diese Abfrage auf einen sehr großen Datensatz anwenden werde, werde ich einige Benchmarks (~ 2m Zeilen) ausführen und die Abfrage akzeptieren, die die beste Antwort liefert. Vielen Dank. – Amo

Verwandte Themen