2016-08-02 3 views
0

Ich muss eine "Verzicht" -Flag in meiner Tabelle für alle außer das neueste Ergebnis pro ID setzen. Ich dachte, ich hätte eine Abfrage, die hier funktioniert, aber wenn ich eine Auswahl für die Abfrage ausfühle, erhalte ich falsche Ergebnisse - ich habe einen Fall gesehen, in dem die beiden einzigen Ergebnisse für eine bestimmte ID ausgewählt wurden. Ich bekomme auch mehrere Ergebnisse mit genau denselben Daten. Was mache ich hier falsch?SQL Wählen Sie alle, aber das neueste Ergebnis pro ID

Hier ist meine select-Anweisung:

select t.test_row_id, t.test_result_id, t.waived, t.pass, t.comment 
from EV.Test_Result 
join EV.Test_Result as t on EV.Test_Result.test_row_id = t.test_row_id and EV.Test_Result.start_time < t.start_time and t.device_id = 1219 and t.waived = 0 
order by t.test_row_id 

Hier ist die eigentliche Abfrage, die ich ausführen möchten:

update EV.Test_Result 
set waived = 1 
from EV.Test_Result 
join EV.Test_Result as t on EV.Test_Result.test_row_id = t.test_row_id and EV.Test_Result.start_time < t.start_time and t.device_id = 1219 and t.waived = 0 
+0

Wenn Sie eine Datumsspalte haben, die einen eindeutigen Zeitstempel (dh es ist mindestens datetime) zurückgibt, dann ist dies mit einer MAX-Funktion einfach. Wenn nicht ... nun, vielleicht ein Designfehler in Ihrer Tabellenstruktur. –

+0

Wir haben eine start_time Spalte, die ein Datetime2 ist. Ich habe auch versucht, Max aber hatte Schwierigkeiten, das richtig geschrieben zu bekommen. Hast du ein Beispiel? Ich bin nicht sehr kompetent in SQL, das ist nicht meine Hauptberufsbeschreibung (obwohl ich versuche zu lernen!). – Johonn

+0

Ein letzter Vorschlag wäre die Formatierung der Abfrage mit Einrückungen. SQL ignoriert Inline-Leerzeichen, die nicht in einer Zeichenfolge enthalten sind (Anmerkung (') ist das Zeichenfolgentrennzeichen in SQL) –

Antwort

1

Wenn ich das richtig verstehe, Sie haben Probleme, weil die Mächtigkeit der ON Prädikat alle passenden Zeilen zurückgibt.

 EV.Test_Result.test_row_id = t.test_row_id 
    and EV.Test_Result.start_time < t.start_time 

Diese ON werden alle der START_TIME Werte vergleichen, die die gleiche ID haben und zurück jede Kombination von Ergebnismengen, wo START_TIME kleiner als die t.start_time ist. Das ist eindeutig nicht das, was du willst.

    and t.device_id = 1219 
        and t.waived = 0 

Dies ist eigentlich ein Prädikat (ON technisch eins ist), aber ich würde dies in einem aus mehreren Gründen subquery/CTE verwenden möchten: Sie begrenzen die Anzahl der Zeilen SQL abgerufen hat und zu vergleichen.

etwas wie das folgende sein könnte, was man benötigt:

SELECT A.test_row_id 
    , A.test_result_id 
    , A.waived 
    , A.pass 
    , A.comment 
FROM EV.Test_Result A 
INNER JOIN (SELECT MAX(start_time) AS start_time 
       , test_row_id 
      FROM EV.Test_Result 
      WHERE device_id = 1219 
       AND waived = 0 
      GROUP BY test_row_id 
          ) AS T ON A.test_row_id = T.test_row_id 
            AND A.start_time < T.start_time 
ORDER BY A.test_row_id 

Diese Abfrage gibt dann eine 1:M Beziehung zwischen den Werten im ON Prädikat, im Gegensatz zu den M:M Abfrage, die Sie laufen hatte.

UPDATE: Da ich meine Abfrage sheepishly vermasselt versucht, auf SO zu ändern, ich werde mich erlösen durch die physischen und logischen Befehle von Grund SQL Abfrage Betreiber erklären:

Wie Sie wissen, Sie schreiben eine einfache SELECT Anweisung wie folgt aus:

SELECT <aggregate column>, SUM(<non-aggregate column>) AS Cost 
FROM <table_name> 
WHERE <column> = 'some_value' 
GROUP BY <aggregate column> 
HAVING SUM(<non-aggregate column>) > some_value 
ORDER BY <column> 

Beachten Sie, dass, wenn Sie eine Aggregatfunktion verwenden, werden alle anderen Spalten in der GROUP BY oder eine andere Funktion erscheinen.

Nun erfordert SQL Server sie in dieser Reihenfolge geschrieben werden, obwohl es dies tatsächlich verarbeitet logisch durch die folgende Reihenfolge, die das Auswendiglernen wert ist:

  • FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY

Es gibt weitere Details über SELECT - MSDN gefunden, aber das ist, warum alle Spalten in dem SELECT Operator in der Gruppe oder in einer Aggregatfunktion sein (SUM, MIN, MAX, etc) ... und auch, warum mein Lazy-Code ist fehlgeschlagen bei deinem ersten Versuch. :/ Beachten Sie auch, dass die ORDER BY zuletzt ist (technisch TOP Operator tritt danach auf), und das ohne es ist das Ergebnis nicht deterministisch, es sei denn eine Funktion wie DENSE_RANK erzwingt es (dachte dies in der SELECT Aussage auftritt).

Hoffe, dies hilft das Problem zu lösen und noch besser, wie SQL funktioniert. Prost

+0

oops, vergessen, die test_row_id in dieser Unterabfrage anzugeben.xD –

+0

Danke für diese sehr informative Antwort, es hilft, ein bisschen besser zu verstehen, was vor sich geht. Ich habe versucht, Ihren Code und Syntaxfehler in der Nähe der WHERE in der Unterabfrage obwohl. Irgendeine Idee, was ist dort falsch? – Johonn

+0

@Johonn LOL ... xD ja ... xD WHERE-Klausel wird falsch gesetzt. xD Ich muss daran denken, meine Abfragen weniger auf SO zu ändern. xD –

0

Können Sie versuchen ROW_NUMBER() Funktion, um durch Zeitstempel absteigenden und ROW_NUMBER mit Werten Ausfiltern 1 ;

Below Abfrage sollte alle Datensätze pro id außer spätestens ein

ich eine Tabelle mit Feldern in Oracle versucht, unter Abfrage holen: id, user_id, record_order adn Zeitstempel und es funktionierte:

select 
      <table_name_alias>.* 
     from 
     (
    select 
    id, 
    user_id, 
     row_number() over (partition by id order by record_order desc) as record_number 
    from 
    <your_table_name> 
) <table_name_alias> 
     where 
    record_number <>1; 

Wenn Sie Teradata DB verwenden, können Sie auch die QUALIFY-Anweisung versuchen. Ich bin mir nicht sicher, ob alle DBs dies unterstützen.

Select 
    table_name.* 
    from table_name 
    QUALIFY row_number() over (partition by id order by record_order desc) <>1; 
+0

Danke für die Antwort. Unter der Annahme, dass "RANK" eine neue Tabelle ist (der Rang scheint ein reserviertes Wort zu sein), kann ich das oben genannte nicht ausführen - die letzte Zeile erzeugt Fehler für mich: "Erwarteter AS, ID oder QUOTED_ID" (für WHERE) und "ungültiger Spaltenname r" (ich benutzte r anstelle von RANK) – Johonn

+0

@Johon dieser Fehler liegt daran, dass die abgeleitete Tabelle einen Alias ​​benötigt. Und ja RANK ist ein reserviertes Wort. RowNum oder R würden bessere Entscheidungen treffen. –

+0

@Johonn. Can u unterhalb Abfrage versuchen, es für mich in Oracle hat Arbeit 'TABLE1 select * from (wählen id, user_id, row_number() über (Partition von id um von record_order ab) als record_number from TABLE_NAME) TABLE1 where record_number <> 1; ' Wenn Sie Teradata DB verwenden, können Sie auch die QUALIFY-Anweisung versuchen. Ich bin mir nicht sicher, ob alle DBs dies unterstützen. 'Wählen * aus table_name QUALIFY row_number() über (Partition nach ID-Reihenfolge von record_order desc) <> 1;' – Leo

Verwandte Themen