Neusten einträge behalten ?
AndreasN
- datenbank
Holla,
ich hatte mal hier: </archiv/2003/12/65322/#m371795> wegen dem Löschen aus einer Tabelle nachgefragt, wie ich es lösen könnte, dass die neusten 30 Einträge behalten werden. Hier wurde mir eigentlich ganz gut geholfen , weil ich mit dem Bsp durch das Sortieren nach unixtime schnell die 30 ältesten Einträge behalten könnte und den Rest löschen könnte ... KÖNNTE , wenn diese Syntax laufen würde:
DELETE FROM test
WHERE recipientid=23 ORDER BY senderid LIMIT 30
Leider bekomme ich dann folgende Fehlermeldung:
#1064 - You have an error in your SQL syntax near 'ORDER BY senderid LIMIT 5 ' at line 1
Habe ich da jetzt einen Denkfehler gemacht oder ist es so dass die MySQL DB den ORDER BY Befehl beim löschen noch nicht kennt ? :-(
Wenn dem so sein sollte, hat jemand noch eine andere Idee wie ich diese Aufgabe lösen könnte, damit alles bis auf die 30 Neusten Messages gelöscht wird ?
Vielen Dank schon im voraus.
MfG
AndreasN
yo,
theoretisch ist das eine unterabfrage. das problem ist, das mysql unterabfragen höchst wahrscheinlich nicht akzeptiert. deshalb könnte man es in zwei schritten lösen. erst eine select anweisung, die das datum des 30. datensatz ermittelt und dann alles löscht, was älter als dieses datum ist.
1. SELECT MIN(timestamp_spalte) FROM test ORDER BY timestamp_spalte ASC LIMIT 30;
2. DELETE FROM test WHERE timestamp_spalte < ergebnis__abfrage;
Ilja
Hello,
theoretisch ist das eine unterabfrage. das problem ist, das mysql unterabfragen höchst wahrscheinlich nicht akzeptiert.
Sieht leider ungefähr so aus. DELETE akzeptiert kein ORDER BY
deshalb könnte man es in zwei schritten lösen. erst eine select anweisung, die das datum des 30. datensatz ermittelt und dann alles löscht, was älter als dieses datum ist.
SELECT MIN(timestamp_spalte) FROM test ORDER BY timestamp_spalte ASC LIMIT 30;
DELETE FROM test WHERE timestamp_spalte < ergebnis__abfrage;
Und Andreas will ja auch 30 Sätze behalten und nicht 30 Sätze löschen. Das LIMIT 30 würde aber die Löschung auf 30 Sätze beschränken und nicht den Rest.
Ich sehe da momentan nur die Möglichkeit, sich mit einem SELECT die IDs der Sätze zu besorgen, die manh behalten will, diese dann in das Löschquery in eine "WHERE ID NOT IN($liste)" Klausel zu packen.
Die Liste ist einfach eine durch Komma getrennte Liste der IDs.
Da bleibt in dynamischen Datenbeständen nur noch das Problem, dass zwischen dem Select und dem Delete frische Sätze hinzukommen könnten, die dann auch mit weg wären. Das kann man dann wahrscheinlich nur nach Deiner Methode der vorherigen Grenzbestimmung machen. Da Timestamp sehr grob ist (es könnten durchaus mehrere Sätze den gleichen Timestamp haben) könnten vielleicht beide Methoden zusammen den gewünschten Erfolg bringen.
Zum Glück muss man es ja nur einmal vernünftig programmieren und dann funktionierts bis in alle Ewigkeit ;-)
Liebe Grüße aus http://www.braunschweig.de
Tom
yo,
Und Andreas will ja auch 30 Sätze behalten und nicht 30 Sätze löschen. Das LIMIT 30 würde aber die Löschung auf 30 Sätze beschränken und nicht den Rest.
nein, das limit löscht erst einmal gar nichts. die erste abfrage dient nur dazu, den kleinsten timestamp der 30. größten timestamps zu bekommen. danach wird alles was kleiner ist gelöscht, sprich keiner der ersten 30. wird gelöscht, da diese alle größer sind.
das einzig kritische ist, ob das Limit auf 30 datensätze auch vor der aggregatfunktion ausgeführt wird.
Ich sehe da momentan nur die Möglichkeit, sich mit einem SELECT die IDs der Sätze zu besorgen, die manh behalten will, diese dann in das Löschquery in eine "WHERE ID NOT IN($liste)" Klausel zu packen.
zu umständlich
Ilja
Hello,
nein, das limit löscht erst einmal gar nichts. die erste abfrage dient nur dazu, den kleinsten timestamp der 30. größten timestamps zu bekommen. danach wird alles was kleiner ist gelöscht, sprich keiner der ersten 30. wird gelöscht, da diese alle größer sind.
Das bezog sich auch nicht auf Dein Select, sondern auf das limitierte DELETE. Das sorgt nämlich dafür, dass nur 30 Sätze gelöscht werden. Leider weiß man nicht welche, da es ja noch kein "Order by" bei "Delete" gibt.
Bei Deiner Methode ist nicht sichergestellt, dass nicht dopplete Timestamps vorhanden sind. Mal verkürt dargestellt:
lfdNr ID Timestamp
----- ----- ---------
1 44 333
2 43 333
3 42 333
4 40 295
5 36 250
6 35 249
7 34 249
8 33 249
9 31 248
10 30 200
11 29 200
Die Möglichkaeit ist in Systemen mit höherem Datenaufkommen sehr wahrscheinlich.
Nun sollen die neuesten 6 Sätze erhalten bleiben.
Nach deiner Methode würde dafür alles gelöscht werden, was einen Timestamp < 249 hat. Also bleiben die Sätze 1-8 übrig. Das sind aber 8.
Man kann sich unschwer vorstellen, dass es sich in Systemen mit hoher Dynamik und vielen gleichzeitigen Nutzern auch um mehr als 2 Sätze zuviel handeln könnte. Zum Glück wirkt sich der Fehler hier zur "sichern Seite" aus.
Als kleinen Beweis für das mehrfache Auftreten der Timestamps stelle ich Dir gerne auf Wunsch einen Auszug aus meinem Logbuch zur Verfügung. Sven Rautenberg hat bei einem Versuch, den er für mich gestartet hatte, im Mittel 135 Zugriffe pro Sekunde an das System abgesetzt und durchführen lassen.
Da man eine bessere Lösung mit ein wenig mehr Nachdenken programmieren kann, halte ich sie auch nicht für zu kompliziert. Faulheit führt immer zu schlechten Programmen.
Liebe Grüße aus http://www.braunschweig.de
Tom
Holla,
Das bezog sich auch nicht auf Dein Select, sondern auf das limitierte DELETE. Das sorgt nämlich dafür, dass nur 30 Sätze gelöscht werden. Leider weiß man nicht welche, da es ja noch kein "Order by" bei "Delete" gibt.
Laut:http://www.mysql.de/documentation/mysql/bychapter/manual.de_Reference.html#IDX1359 gibt es aber doch ein ORDER BY bei DELETE allerdings erst ab MySQL 4.0
Die Methode von Ilja klingt schon mal ganz gut, wenn dort nicht der besagte Nachteil wäre, dass bei gleichem Timestamp nur bsw 8 Messages gelöscht werden würden. ABER:
Da das ein Kurzmessages System ist, und nicht GENERELL nur die neusten 30 behalten werden sondern PRO user nur die neusten 30 sollte das denke ich kein Problem darstellen. Es müsste schon mit dem Teulfel zugehen , dass jemand zur gleichen Sekunde von verschiedenen Leuten eine Message bekommt. Man darf sich das nicht als Messenger ala ICQ vorstellen dass alle 2 Sekunden eine Nachricht kommt, sondern eine Art Telegram-System wo die Leute statt einer Mail mal eine Nachricht an den User hinterlassen können.
Ich denke die o.g. Methode sollte ausreichen oder was haltet Ihr davon ??
Auf jeden Fall schon mal vielen herzlichen Dank für den Denkanstoß ;)
Da man eine bessere Lösung mit ein wenig mehr Nachdenken programmieren kann, halte ich sie auch nicht für zu kompliziert. Faulheit führt immer zu schlechten Programmen.
Ich glaube da hast Du mir jetzt eine Denkaufgabe zu Sylvester gestellt :D Oder hilfst Du mir da etwas auf die Sprünge ? Faul bin ich generell, manchmal schaff ich es jedoch es abzustellen :)
Euch allen schon mal einen guten Rutsch ins neue Jahr !
MfG
AndreasN
Hello,
Da man eine bessere Lösung mit ein wenig mehr Nachdenken programmieren kann, halte ich sie auch nicht für zu kompliziert. Faulheit führt immer zu schlechten Programmen.
Ich glaube da hast Du mir jetzt eine Denkaufgabe zu Sylvester gestellt :D Oder hilfst Du mir da etwas auf die Sprünge ? Faul bin ich generell, manchmal schaff ich es jedoch es abzustellen :)
Nun war ich selber unterwegs und habe jetzt hier noch eigene Dinge zu tun. Aber grundsätzlich solltest Du schon den Weg der Diskussion über das Forum gehen. Es lohnt sich immer, seine Gedanken mit anderen auszutauschen, wenn sie denn nicht nur seit Ewigkeiten vererbte fehlerhafte Theorien proklamieren, sondern acuh bereit sind, sich mal (betrachtungshalber) auf den anderen einzulassen. Ich finde, dass Diskussionen am meisten davon profitieren, dass man versucht, den Anderen bei seinen Gedanken zu unterstützen und dabei dann "gemeinsam" die Fehler entdeckt.
Liebe Grüße aus http://www.braunschweig.de
Tom
yo,
Das sorgt nämlich dafür, dass nur 30 Sätze gelöscht werden. Leider weiß man nicht welche, da es ja noch kein "Order by" bei "Delete" gibt.
nein, es werden alle datensätze gelöscht, die ein kleineren timestamp haben. damit ist nichts über die anzahl der datensätze gesagt. es können 0, 100 oder 10.000 gelöscht werden.
Bei Deiner Methode ist nicht sichergestellt, dass nicht dopplete Timestamps vorhanden sind. Mal verkürt dargestellt:
gleiche timestamps haben keinen negativen einfluss auf die methode, da nichts weiter spezifiziert wurde.
Nun sollen die neuesten 6 Sätze erhalten bleiben.
Nach deiner Methode würde dafür alles gelöscht werden, was einen Timestamp < 249 hat. Also bleiben die Sätze 1-8 übrig. Das sind aber 8.
erstens trifft das problem nur auf, wenn es mehrere datensätze mit den gleichen timestamp bei der untergrenze gibt, was es weiter unwahrscheilich macht. und zum anderen ist nicht weiter spezifiziert, wie bei gleicheit vorgegangen werden soll. gibt es mehrere davon, könnte zum beispiel die laufende nummer mit rangezogen werden, um eine auswhl bei gleichen timestamps zu treffen. dies ist aber nicht geschehen und somit können gleiche datensätze auch nicht weiter unterschieden werden und müssen sogar bestehen bleiben.
Da man eine bessere Lösung mit ein wenig mehr Nachdenken programmieren kann, halte ich sie auch nicht für zu kompliziert. Faulheit führt immer zu schlechten Programmen.
nein, das problem wird vollständig gelöst. sollte auch die gleichheit erfasst werden, kann man dies ebenfalls erreichen, ohne eine komplette liste mit ids zu speichern. ist es egal, welchen der datensätze mit gleichen timestamp man behält, löscht man im zweiten schritt beim delete alle die kleiner sind und alle datensätze mit dem gleichen timestamp (untergrenze) bis auf einem (WHERE spalte1 < ×tamp OR spalte1=$timestamp AND spalte_ID <> $id). und dann bekommt man in deinem besipiel auch auf 6 datensätze raus.
Ilja
Hello,
Das sorgt nämlich dafür, dass nur 30 Sätze gelöscht werden. Leider weiß man nicht welche, da es ja noch kein "Order by" bei "Delete" gibt.
nein, es werden alle datensätze gelöscht, die ein kleineren timestamp haben. damit ist nichts über die anzahl der datensätze gesagt. es können 0, 100 oder 10.000 gelöscht werden.
Schade, dass Du mich gar nicht verstehen willst.
DELETE from $table where timestamp_col < $timestamp limit 30;
Das war der ursprüngliche Ansatz. Ich habe nur das "order by" herausgenommen, weil MySQL DAS nicht unterstützt. Das "limit 30" wird aber sehr wohl unterstützt. Und nun wiederhole ich meine Aussage von oben:
"limit 30" nützt in diesem Statement mit der gegebenen Aufgabenstellung aber nichts, da ja
1. nicht 30 Sätze gelöscht werden sollten, sondern übrig bleiben sollten
2. man bei "limit 30" nicht genau weiß, welche Sätze gelöscht werden,
da das "order by" in DELETE nicht funktioniert.
Man kann in dynamischen Datenbeständen mit MySQL also meistens nur mit einer zweistufigen Technik arbeiten. 1. Stufe = Selektieren und externes Merken von Primaries, 2. Stufe Durchführung der eigentlichen Aktion. Das kann dann bei großen Datenbeständen beliebig kompliziert werden, damit die Operation überhaupt über PHP durchführbar ist und kann auch das Locking von Tabellen erforderlich machen. Da in der Praxis aber meistens keine einheitlichen Zugriffsmethoden vorhanden sind, kann man das mit dem Locking vergessen, da es nur advisory ("softlock") ist und nicht mandatory ("hardlock").
Grüße aus http://www.braunschweig.de
Tom
yo,
frohes neues.
Schade, dass Du mich gar nicht verstehen willst.
wollen schon, können nicht. aber ich arbeite dran.
"limit 30" nützt in diesem Statement mit der gegebenen Aufgabenstellung aber nichts, da ja
1. nicht 30 Sätze gelöscht werden sollten, sondern übrig bleiben sollten
wir verfolgen da zwei verschiedene ansätze. mit meiner ersten selectabfrage will ich gar keine datensätze löschen, sondern den 30. höchsten timestamp erwischen. das ist praktisch der zeitpunkt, der die tabelle teilt. alles was größer ist, bleibt erhalten. alles was kleiner ist, wird gelöscht. wie mit datensätze mit gleichen timestamps zu verfahren ist, muss halt noch genau spezifiziert werden. im falle von andreas können die gleichen ruhig auch erhalten bleiben.
durch meinen anderen ansatz. brauche ich das LIMIT gar nicht zum löschen von datensätzen. wenn ich den gesuchten timestamp erst einmal dingfest gemacht habe, sage ich einfach, lösche mir alle, die kleiner als dieser sind. das LIMIT in meinen fall brauche ich also nur, zum auffinden des gesuchten timestamps, nicht aber zum löschen von datensätzen.
2. man bei "limit 30" nicht genau weiß, welche Sätze gelöscht werden,
da das "order by" in DELETE nicht funktioniert.
ist wie oben beschrieben nicht von nöten bei meinem ansatz.
Ilja
Holla,
erst mal ein FROHES NEUES JAHR !
Jetzt hab ich das genauso probiert, wie Du das vorgeschlagen hattest, allerdings bekomme ich nicht den richtigen Wert:
SELECT MIN( sendtime ) FROM test WHERE recipientid =23 ORDER BY sendtime ASC LIMIT 30;
Gibt bei mir den absolut niedrigsten Wert von "sendtime" heraus. Also funktioniert es da nicht, dass ich somit den 30ten Timestamp von hinten bekomme.
Hast Du vielleicht eine Idee was ich damit falsch gemacht habe?
Vielen Dank im voraus.
MfG
AndreasN
yo,
auch dir ein frohes neues.
Gibt bei mir den absolut niedrigsten Wert von "sendtime" heraus. Also funktioniert es da nicht, dass ich somit den 30ten Timestamp von hinten bekomme.
das habe ich befürchtet, dann wird die aggregatfunktion vor dem limit ausgeführt. zwei möglichkeiten fallen mir spontan ein.
entweder eine temporäre tabelle, sprich die gleiche abfragen, aber erst einmal ohne aggretagfunktion, und dann mit einer zweiten abfrage über die temp tabelle mit aggregat min(). dann solltest du den kleinsten der 30 haben.
oder aber über du ließt alle 30 datensätze wie oben aus und schaust zum beispiel mit php, welcher der 30 datensätze den niedrigsten timestamp hat. ist in prinzip das gleiche, nur nicht mit temporären tabellen.
Ilja
Hello,
oder aber über du ließt alle 30 datensätze wie oben aus und schaust zum beispiel mit php, welcher der 30 datensätze den niedrigsten timestamp hat. ist in prinzip das gleiche, nur nicht mit temporären tabellen.
Oder unter Missachtung der bereits diskutierten Fehlermöglichkeiten nimmst Du dieses Statement (sinngemäß):
Select ID_TEST, UPDATETIME1 from TEST order by UPDATETIME1 desc limit 29, 1;
Das liefert Dir die Werte des den 30. Satzes. Dann kannst Du anschließend alle Sätze, die älter sind, löschen. Wie gesagt, da könnten dann auch locker mal 130+30=160 Sätze übrig bleiben, statt der gewünschten 30.
Liebe Grüße aus http://www.braunschweig.de
Tom
yo,
Select ID_TEST, UPDATETIME1 from TEST order by UPDATETIME1 desc limit 29, 1;
super, das macht es einfach und performant, den 30 datensatz zu finden.
Das liefert Dir die Werte des den 30. Satzes. Dann kannst Du anschließend alle Sätze, die älter sind, löschen.
ganz genau das ist der lösungsansatz.
Wie gesagt, da könnten dann auch locker mal 130+30=160 Sätze übrig bleiben, statt der gewünschten 30.
das muss kein nachteil sein, dass datensätze mit gleichen timestamps vorkommen. je nachdem wie man diesen fall behandeln will, kann man dafür lösungen finden. in falle von andreas können datensätze mit gleichen datum bestehen bleiben, zumal es sich auch nur auf die unter grenze bezieht. wichtig ist, das andreas was in der hand hat, mit dem er zufrieden ist.
Ilja
Holla,
Select ID_TEST, UPDATETIME1 from TEST order by UPDATETIME1 desc limit 29, 1;
Oh Tom SUPER das war genau das was ich wollte. Zu dumm dass man selbst nicht darauf kommt, vor allem weil die Lösung vergleichsweise dann ja sehr einfach ist :)
Das liefert Dir die Werte des den 30. Satzes. Dann kannst Du anschließend alle Sätze, die älter sind, löschen. Wie gesagt, da könnten dann auch locker mal 130+30=160 Sätze übrig bleiben, statt der gewünschten 30.
Das ist ok, ich werde definitiv auslesen ob mehrer Datensätze mit dem gleichen Timestamp vorhanden sind und dann .... oder ....... Ich glaube das kann ich genau so lösen ;)
Ich danke Euch vielmals. Ihr habt mir sehr geholfen :-)
MfG
AndreasN