94451
Goto Top

MySQL Doppelte elemente löschen! Ältestes element beibehalten!

Hallo,

ich habe Grundlegende PHP und MySQL fähigkeiten aber dafür reichts dann leider doch nicht!

ich habe eine Tabelle und die schaut ca. so aus!


ID |. Auftrags_NR | Unterauftrag | Status | LASTCHANGE
1 .|. 123 ..............| 1 ...............| 100 .....| 2012-06-19 07:49:12
2 .|. 123 ..............| 1 ...............| 100 .....| 2012-06-19 07:51:10
3 .|. 123 ..............| 1 ...............| 100 .....| 2012-06-19 07:52:05
4 .|. 125 ..............| 1 ...............| 100 .....| 2012-06-19 08:01:55
5 .|. 125 ..............| 1 ...............| 200 .....| 2012-06-19 08:02:12


ID -> INDEX,... immer anders in diesen Fall uninsteressant!

Die Schlüsselwerte für das Suchen und Löschen sind Auftrags_Nr, Unterauftrag und Status! LASTCHANGE ist das Datum andem der Datensatz das letzte mal geändert wurde face-smile (klingt komisch, ist aber so).

Ich suche eine Funktion die feststellt das (wie im Beispiel) ID 1, 2 und 3 gleich sind und dann alle doppelten Werte löscht (außer dem ältesten -> ID 1).

mein ansatz:
$delete_twice = "DELETE FROM tabelle WHERE ... ORDER BY LASTCHANGE ASC LIMIT 1, 99";
Aber irgendwie find ich keine richtige bedingung!

Vielen Dank für euere Unterstützung

Beste Grüße
RoadRunner
Kommentar vom Moderator Biber am Jun 22, 2012 um 14:02:05 Uhr
Verschoben von PHP nach Datenbanken.

Content-Key: 186731

Url: https://administrator.de/contentid/186731

Printed on: April 23, 2024 at 10:04 o'clock

Member: nxclass
nxclass Jun 20, 2012 updated at 06:52:11 (UTC)
Goto Top
außer dem ältesten
.. sagen wir mal: "dem ersten" ?

Du brauchst eine Liste mit allen "ersten" IDs.
SELECT * FROM Tabelle
GROUP BY Auftrags_NR
..sollte schon reichen, da MySQL wohl immer die nicht im GROUP oder in einer Aggregat Funktion gepackten Felder, den ersten gefundenen Datensatz zurück gibt.

Nun machst mit dieser Ergebnismenge ein LEFT JOIN über die Tabelle und prüfst auf NULL
SELECT ID FROM Table t1 LEFT JOIN (
  SELECT * FROM Tabelle
  GROUP BY Auftrags_NR
) t2 ON (t1.ID = t2.ID)
WHERE t2 IS NULL

nun hast du die Liste der zu löschenden Datensätze
DELETE FROM Table WHERE ID IN (
  SELECT ID FROM Table t1 LEFT JOIN (
    SELECT * FROM Tabelle
    GROUP BY Auftrags_NR
  ) t2 ON (t1.ID = t2.ID)
  WHERE t2 IS NULL
)

..ungetestet - so ich arbeite mal weiter...

EDIT: gehört eigentlich zu MySQL / Datenbanken - dieses Problem
Member: godlie
godlie Jun 20, 2012 at 06:56:24 (UTC)
Goto Top
Also,
ein Group BY ohne ORDER wäre in diesem Fall sehr fatal.
Mitglied: 94451
94451 Jun 20, 2012 at 07:40:14 (UTC)
Goto Top
Danke erstmal!

.. sagen wir mal: "dem ersten" ?

naja, ich meinte aber schon den ältesten (nach LASTCHANGE) - da es theoretisch möglich ist das einer der ersten bearbeitet wird und dann eine höhere ID, einen älteren Wert hat -> Klinkt komisch ist aber so!

Des weiteren kann es sein, das ich zwar die selbe Auftrags_NR habe, aber einen anderen unterauftrag! diese sollten dann nicht gelöscht werden! ebenso mit der Status ID

letztendlich möchte ich immer den maximalältesten (LASTCHANGE) Wert von jeder Auftrags_NR, Unterauftrag und Status Kombination haben!

Vielen Dank
Mitglied: 94451
94451 Jun 20, 2012 at 07:42:13 (UTC)
Goto Top
Zitat von @godlie:
Also,
ein Group BY ohne ORDER wäre in diesem Fall sehr fatal.

Das heißt?
-> Welche auswirkungen hat ein ODER auf Group By? Und wie mach ichs richtig?
Member: godlie
godlie Jun 20, 2012 at 07:46:28 (UTC)
Goto Top
Ein GROUP BY <-- wie der name sagt Gruppiert etwas nach einem Kriterium
Ein ORDER BY <-- sortiert etwas nach einem gewissem Kriterium
Mitglied: 94451
94451 Jun 20, 2012 at 07:55:04 (UTC)
Goto Top
ja OK,... aber warum sollte es fatal sein, wenn ich nicht sortiere wärend ich gruppiere?

und, ja leider bekomm ichs nicht hin!

wie genau muss ich die Abfrage aufbauen?
Member: LianenSchwinger
LianenSchwinger Jun 20, 2012 updated at 09:31:32 (UTC)
Goto Top
Hallo,

ich denke es sollte wie folgt funktionieren:

DELETE FROM tabelle c
WHERE c.ID NOT IN (SELECT a.ID 
                   FROM tabelle a
                   INNER JOIN (SELECT t.AUFTRAGS_NR, t.UNTERAUFTRAG, t.STATUS, MIN(t.LASTCHANGE) AS LC
                               FROM tabelle t
                               GROUP BY t.AUFTRAGS_NR, t.UNTERAUFTRAG, t.STATUS) b 
                   ON a.AUFTRAGS_NR = b.AUFTRAGS_NR
                   AND a.UNTERAUFTRAG = b.UNTERAUFTRAG
                   AND a.STATUS = b.STATUS
                   AND a.LASTCHANGE = b.LC)

Gruß Jörg
Member: LianenSchwinger
LianenSchwinger Jun 20, 2012 at 08:14:24 (UTC)
Goto Top
Zitat von @94451:
ja OK,... aber warum sollte es fatal sein, wenn ich nicht sortiere wärend ich gruppiere?

und, ja leider bekomm ichs nicht hin!

wie genau muss ich die Abfrage aufbauen?

... man Sortiert nicht während des Gruppierens.
Sondern es werden die Datensätze nach dem Gruppieren sortiert.

Von daher glaube ich nicht, dass die Aussage aus dem 1. Post

..sollte schon reichen, da MySQL wohl immer die nicht im GROUP oder in einer Aggregat Funktion gepackten Felder, den > ersten gefundenen Datensatz zurück gibt.

funktioniert.

Gruß Jörg
Member: nxclass
nxclass Jun 20, 2012 updated at 10:00:22 (UTC)
Goto Top
ein Group BY ohne ORDER wäre in diesem Fall sehr fatal.
... man Sortiert nicht während des Gruppierens.
Sondern es werden die Datensätze nach dem Gruppieren sortiert.
... ein GROUP löst autom. auch ein ORDER über die angegebenen Felder aus.
Man müsste sogar:
GROUP BY Feld
ORDER BY NULL
schreiben um ein Sortieren zu verhindern (für bessere Performance ggf sinnvoll)
ABER: das ORDER nach dem GROUP ist eh nicht wichtig - wenn dann müsste das ORDER vor dem Gruppieren gemacht werden.

Das Beispiel sollte funktionieren, WENN: die Daten in LASTCHANGE auch der Reihenfolge beim Speichern in der DB entsprechen. Ansonsten muss man vorher noch ein SUB SELECT mit ORDER ausführen.

EDIT: in Post 1 ist ein Fehler:
/* SELECT * FROM Table WHERE ID IN ( */
DELETE FROM Table WHERE ID IN (
  SELECT t1.ID FROM Table t1 LEFT JOIN (
    SELECT * FROM Tabelle
    GROUP BY Auftrags_NR
  ) t2 ON (t1.ID = t2.ID)
  WHERE t2.ID IS NULL
)

mit Verbesserung:
/* SELECT * FROM Table WHERE ID IN ( */
DELETE FROM Table WHERE ID IN (
  SELECT t1.ID FROM Table t1 LEFT JOIN (
    SELECT tmp.* FROM ( SELECT * FROM Table ORDER BY LASTCHANGE ) tmp
    GROUP BY tmp.Auftrags_NR
  ) t2 ON (t1.ID = t2.ID)
  WHERE t2.ID IS NULL
)
Mitglied: 94451
94451 Jun 20, 2012 at 09:42:06 (UTC)
Goto Top
Zitat von @LianenSchwinger:
Hallo,

ich denke es sollte wie folgt funktionieren:

> DELETE FROM tabelle
> WHERE ID NOT IN (SELECT ID 
>                  FROM tabelle a
>                  INNER JOIN (SELECT AUFTRAGS_NR, UNTERAUFTRAG, STATUS, MIN(LASTCHANGE) LC
>                              FROM tabelle
>                              GROUP BY AUFTRAGS_NR, UNTERAUFTRAG, STATUS) b 
>                     ON a.AUFTRAGS_NR = b.AUFTRAGS_NR
>                     AND a.UNTERAUFTRAG = b.UNTERAUFTRAG
>                     AND a.STATUS = b.STATUS
>                     AND a.LASTCHANGE = b.LC)
> 

Gruß Jörg

leider gibt er mir nur:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM cd_status WHERE ID NOT IN (SELECT ID FROM cd_status a ' at line 1  

Zurück.

Woran kann es liegen?
Member: nxclass
nxclass Jun 20, 2012 at 09:47:48 (UTC)
Goto Top
Da fehlt die Orginal SQL Anweisung. Wenn du aus dem DELETE ein SELECT gemacht hast, ist evtl nur ein , zuviel !?
Member: LianenSchwinger
LianenSchwinger Jun 20, 2012 updated at 09:59:10 (UTC)
Goto Top
... poste bitte mal Deine komplette Anweisung.

Könnte sein, das Du vor dem FROM ein "," stehen hast.
Mitglied: 94451
94451 Jun 20, 2012 at 12:59:17 (UTC)
Goto Top
                               $delete_twice = "  
SELECT FROM cd_status
WHERE ID NOT IN (SELECT ID 
                 FROM cd_status a
                 INNER JOIN (SELECT Auftrags_NR, Unterauftrag, Status, MIN(LASTCHANGE) LC
                             FROM cd_status
                             GROUP BY Auftrags_NR, Unterauftrag, Status) b 
                    ON a.Auftrags_NR = b.Auftrags_NR
                    AND a.Unterauftrag = b.Unterauftrag
                    AND a.Status = b.Status
                    AND a.LASTCHANGE = b.LC)
                               ";  
                               
                               $delete = mysql_query($delete_twice) or die(mysql_error());

aber auch

                               $delete_twice = "  
SELECT * FROM Table WHERE ID IN ( 
  SELECT t1.ID FROM Table t1 LEFT JOIN (
    SELECT tmp.* FROM ( SELECT * FROM Table ORDER BY LASTCHANGE ) tmp
    GROUP BY tmp.Auftrags_NR
  ) t2 ON (t1.ID = t2.ID)
  WHERE t2.ID IS NULL
)
                               ";  
                               
                               $delete = mysql_query($delete_twice) or die(mysql_error());

gibt mir folgenden Fehler zurück

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Table WHERE ID IN ( SELECT t1.ID FROM Table t1 LEFT JOIN ( SELECT tmp.*' at line 1
Member: LianenSchwinger
LianenSchwinger Jun 20, 2012 updated at 13:26:18 (UTC)
Goto Top
... bei der oberen fehlt der "*" nach dem SELECT und bei der unteren hast Du das reservierte Wort "Table" verwendet.

SELECT c.* FROM cd_status c
WHERE c.ID NOT IN (SELECT a.ID 
                   FROM cd_status a
                   INNER JOIN (SELECT t.AUFTRAGS_NR, t.UNTERAUFTRAG, t.STATUS, MIN(t.LASTCHANGE) AS LC
                               FROM cd_status t
                               GROUP BY t.AUFTRAGS_NR, t.UNTERAUFTRAG, t.STATUS) b 
                   ON a.AUFTRAGS_NR = b.AUFTRAGS_NR
                   AND a.UNTERAUFTRAG = b.UNTERAUFTRAG
                   AND a.STATUS = b.STATUS
                   AND a.LASTCHANGE = b.LC)

Habe gerade auf meiner Oracle-Datenbank genau die Anweisung ohne Fehlermeldung abgesetzt.

SELECT c.* FROM ifsapp.inventory_transaction_hist c
WHERE c.transaction_id NOT IN (SELECT a.transaction_id
                               FROM ifsapp.inventory_transaction_hist a
                               INNER JOIN (SELECT t.order_no, t.release_no, t.line_item_no, 
                                                  MIN(t.date_created) AS LC
                                           FROM ifsapp.inventory_transaction_hist t
                                           GROUP BY t.order_no, t.release_no, t.line_item_no) b 
                               ON a.order_no = b.order_no
                               AND a.release_no = b.release_no
                               AND a.line_item_no = b.line_item_no
                               AND a.date_created = b.LC)

Gruß Jörg
Mitglied: 94451
94451 Jun 20, 2012 at 13:36:11 (UTC)
Goto Top
Jaaaaaaa,... es geht!

ich werds mal nochmal genau testen (da es immerhin einige tausend datensätze sind die ich dann lösche) aber es schaut gut aus!


Zitat von @LianenSchwinger:
... bei der oberen fehlt der "*" nach dem SELECT und bei der unteren hast Du das reservierte Wort "Table"
verwendet.

-> hm,.... ja ich bin eh dafür das blödheit bestraft wird ^^

sorry mein fehler!

Danke erstmal für eure Hilfe!
Member: LianenSchwinger
LianenSchwinger Jun 20, 2012 updated at 13:44:36 (UTC)
Goto Top
... welche Version funktioniert denn jetzt, wenn beide, geben sie die selben Daten aus?

Und nicht vergessen die Frage als "gelöst" markieren. face-smile

Gruß Jörg
Mitglied: 94451
94451 Jun 21, 2012 at 07:42:56 (UTC)
Goto Top
Zitat von @LianenSchwinger:
... welche Version funktioniert denn jetzt, wenn beide, geben sie die selben Daten aus?

ich verwende jetzt die da!

		$delete_twice = "  
		SELECT c.* FROM cd_status c
		WHERE c.ID NOT IN (SELECT a.ID 
		FROM cd_status a
		INNER JOIN (SELECT t.Auftrags_NR, t.Unterauftrag, t.Status, MIN(t.LASTCHANGE) AS LC
		FROM cd_status t
		GROUP BY t.Auftrags_NR, t.Unterauftrag, t.Status) b 
		ON a.Auftrags_NR = b.Auftrags_NR
		AND a.Unterauftrag = b.Unterauftrag
		AND a.Status = b.Status
		AND a.LASTCHANGE = b.LC)";  

und die gibt auch das richtige aus!

allerdings hab ich noch n problem,... denn wenn ich das jetzt so abändere (SELECT wird durch DELETE ausgetauscht)

		$delete_twice = "  
		DELETE c.* FROM cd_status c
		WHERE c.ID NOT IN (SELECT a.ID 
		FROM cd_status a
		INNER JOIN (SELECT t.Auftrags_NR, t.Unterauftrag, t.Status, MIN(t.LASTCHANGE) AS LC
		FROM cd_status t
		GROUP BY t.Auftrags_NR, t.Unterauftrag, t.Status) b 
		ON a.Auftrags_NR = b.Auftrags_NR
		AND a.Unterauftrag = b.Unterauftrag
		AND a.Status = b.Status
		AND a.LASTCHANGE = b.LC)";  
gibt er mir
You can't specify target table 'c' for update in FROM clause  
zurück
Member: LianenSchwinger
LianenSchwinger Jun 21, 2012 at 07:52:51 (UTC)
Goto Top
... lass mal das c.* nach dem DELETE weg. face-smile

Gruß Jörg
Mitglied: 94451
94451 Jun 21, 2012 at 08:24:10 (UTC)
Goto Top
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'c WHERE c.ID NOT IN (SELECT a.ID FROM cd_status a INNER JOIN (SELECT t' at line 1

Beste Grüße
RoadRunner
Member: LianenSchwinger
LianenSchwinger Jun 21, 2012 updated at 08:36:00 (UTC)
Goto Top
Hallo RoadRunner,

MySQL scheint keinen Alias für die Tabelle aus der gelöscht werden soll zu mögen, versuch mal:

DELET FROM cd_status
WHERE ID NOT IN (...)

Gruß Jörg
Mitglied: 94451
94451 Jun 21, 2012 at 08:39:42 (UTC)
Goto Top
		$delete_twice = "  
		DELETE FROM cd_status
		WHERE ID NOT IN (SELECT a.ID 
		FROM cd_status a
		INNER JOIN (SELECT t.Auftrags_NR, t.Unterauftrag, t.Status, MIN(t.LASTCHANGE) AS LC
		FROM cd_status t
		GROUP BY t.Auftrags_NR, t.Unterauftrag, t.Status) b 
		ON a.Auftrags_NR = b.Auftrags_NR
		AND a.Unterauftrag = b.Unterauftrag
		AND a.Status = b.Status
		AND a.LASTCHANGE = b.LC)";  

You can't specify target table 'cd_status' for update in FROM clause  

tut mir leid!... hab ich ne falsche MySQL version? oder warum bekomm ichs net hin?
Member: LianenSchwinger
LianenSchwinger Jun 21, 2012 updated at 09:31:44 (UTC)
Goto Top
OK, er scheint kein Subselect mit der Tabelle zu mögen, aus der Zeilen gelöscht werden sollen.
Das heißt, Du musst Dir die zu löschenden ID mittels

CREATE TABLE tmp_cd_status_id 
SELECT c.ID from cd_status c
WHERE c.ID NOT IN (...)

in eine temporäre Tabelle speichern und dann ...

DELETE FROM cd_status
WHERE ID IN (SELECT ID FROM tmp_tabelle)

Am Ende noch weg mit der Temp-Tabelle

DROP TABLE tmp_cd_status_id

Gruß Jörg
Mitglied: 94451
94451 Jun 21, 2012 at 10:24:02 (UTC)
Goto Top
Sehr schön!

Vielen Dank!
Member: nxclass
nxclass Jun 21, 2012 updated at 19:12:28 (UTC)
Goto Top
You can't specify target table 'cd_status' for update in FROM clause
hab ich ne falsche MySQL version?
er scheint kein Subselect mit der Tabelle zu mögen,
... es handelt sich wohl um MyISAM Tabellen - diese sind zwar schnell beim ändern von Daten, müssen dazu aber die betreffende Tabelle sperren. Ein SELECT während des UPDATEs / DELETEs ist somit nicht möglich.

EDIT:
Sollte es sich um sehr viele zu löschende Daten handeln, könnte man die zu erhaltenen Daten auch in die temp. Tabelle übertragen. Danach ein TRUNCATE ausführen (TRANCATE ist sehr viel schneller) und anschließend die Daten wieder einspielen.