PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Datumsabhängiges Inkrementieren bei neuem Datensatz...


BlindCrazyBoy
24.02.2003, 22:47:29
Hallöle Zusammen,

stehe wieder mal vor einem (für meine Begriffe) hammerartigen Problem...

Situationsbeschreibung: Bei uns im Betrieb werden alle Postein- und Postausgänge (Brief, Fax, eMail) in einem Postbuch (handschriftlich und auf echtem Papier :-) vermerkt. Der Azubi (also ich) soll das ganze nun edv-basiert umsetzen. Allerdings bestehen gewisse Anforderungen, die erreicht werden müssen. Zum Beispiel Suchfunktionen im kompletten Bestand (über mehrere Jahr(zehnte) oder ein Wieder-Beginn bei 1 (Inkrementierung) wenn ein neues Jahr begonnen hat.
Nachdem eine jahresübergreifende Suche Voraussetzung ist halte ich es unsinnig für jedes Jahr eine neue Tabelle in der Datenbank (MySQL) anzulegen. Ich habe mir für eine Tabelle mal folgende Struktur überlegt:

ID INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
^^eindeutige Identifizierung eines Datensatzes
Nummer SMALLINT UNSIGNED NOT NULL
^^fortlaufende Nummer, Beginn bei 1 in jedem neuen Jahr
Datum INT UNSIGNED NOT NULL
^^Datum des Schreibens, abgelegt als Unix-Timestamp

Nun stellt sich mir folgendes Problem: Nachdem alle Datensätze jahresunabhängig in der Tabelle rumgeistern, jedoch das Datenfeld 'Nummer' mehr oder weniger automatisch beim Anlegen eines neuen Datensatzes ermittelt werden soll, so weiß ich nicht wie man das in einer Routine umsetzen könnte.

Beispiel: Es befinden sich bereits 234 Datensätze vom Jahr 2002 in der Datenbank. Demnach steht der letzte Wert im Datenfeld 'ID/'Nummer' auf 234. Wird nun im Jahre 2003 ein neuer Datensatz hinzugefügt steht der Wert im Datenfeld 'ID' logischerweiße auf 235, jedoch soll im Feld 'Nummer' wieder mit 1 begonnen werden.

Ich muss auf jeden Fall eine Abfrage haben, die mir den Maximalwert im Datenfeld 'Nummer' abhängig vom Jahr zurückgibt. Arbeite ich mit der MySQL-Funktion MAX() wird mir ja der größte Wert unabhängig von den Jahren zurückgegeben und das ist ja nicht brauchbar.



Falls mir jemand folgen konnte und vor einem ähnlichem Problem gestanden ist, so möge derjenige mir in irgendeiner Form weiterdenken helfen ;-)

Einstweilen schon mal ergeben Dank fürs Lesen *g*


MfG Manu
Tschaui

Greg G
25.02.2003, 08:18:37
Ich denke mal, dass die Ansätze OK sind.

Die Funktion MAX kannst du sehr wohl benutzen, wenn du mit einer where-Klausel das Datum einschränkst. Für die Jahreszahl würde ich übrigens ein neues Feld benutzen, und nicht (nur) den Unix-Timestamp.

Du kannst auch mal eine Abfrage wie
select max(Nummer) as LetzteNummer from Tabelle group by Jahr
probieren, die gibt dir für jedes Jahr den maximalen Nummerwert.

Wichtig ist auch, dass du beim Berechnen der neuen Nummer vorher die Tabelle sperrst, so dass sich nicht zwei fast zeitgleiche Prozesse dieselbe Nummer ziehen. Nach dem Eintragen gibst du dann die Tabelle wieder frei.

GG

BlindCrazyBoy
25.02.2003, 22:35:03
Heyho Greg,

Du hattest Recht mit der WHERE Klausel und der MAX() Funktion. Habe die folgende SQL-Abfrage formuliert:
>>select max(nummer) from tabelle where datum < now() and datum > 1041375600<< (1041375600: 01.01.2003)

Allerdings kann ich der zweiten SQL-Abfrage (mit GROUP) nicht so ganz folgen?!?

Wie setze ich denn das Sperren und Freigeben der Tabelle in MySQL bzw. PHP um?

Mir ist heute noch ein Fall begegnet, welcher die Sache nochmal um einiges komplizierter macht: Ein Mitarbeiter möchte einen Eintrag hinzufügen, welcher vom Datum her ins Vorjahr einzuordnen ist *kopfzerbrech*

Ob es die Spalte mit dem Jahr wirklich braucht? Ich finde die Sache mit dem Timestamp eigentlich ausreichend ;-) Davon abgesehen, dass man mit der Zahl nicht gleich etwas anfangen kann sondern erst umrechnen lassen muss finde ich es nicht weiter schwer :o)


MfG Manu
Tschaui

Greg G
26.02.2003, 08:25:02
Morgen Manu

Zu group by
http://www.mysql.com/doc/de/Group_by_functions.html

Zum Sperren:
http://www.mysql.com/doc/de/Internal_locking.html

Die Spalte mit dem Jahr würde dir zusammen mit der laufenden Jahresnummer einen Sekundärschlüssel geben.
Du könntest diesen als Index (unique) deklarieren, so dass du keine falschen Eingaben machen kannst, bzw. eine Fehlermeldung bekommst, wenn du es versuchst.
Es wäre sehr leicht und auch schneller, alle Einträge für ein Jahr auszugeben.

Wo ist das Problem? Wenn jemand einen Eintrag für 2002 machen will, dann kriegt er halt die nächste Nummer für 2002. Du musst natürlich den Unix-Timestamp für das entsprechende Datum im Jahr 2002 berechnen und kannst nicht now() nehmen.
Schlimm wird es nur, wenn auch die Anforderung besteht, Datensätze löschen zu dürfen. Dann klappt der Ansatz nämlich so nicht mehr. Dann müsstest du dir (am besten in einer weiteren Tabelle) merken, welche Nummer für ein Jahr zuletzt vergeben wurde.

GG

BlindCrazyBoy
26.02.2003, 22:57:44
Hi Greg,

hab mir zwar die Dokumentation zum Sperren von Tabellen durchgelesen doch irgendwie kann ich die Sache nicht umsetzen ;-(

Ansonsten hab ich testweise das Datenfeld 'Datum' in den Datentyp DATE (JJJJ-MM-DD) umgemodelt und folgende Abfrage definiert:
>>select max(Nummer) as LetzteNummer, year(Datum) as Jahr from tabelle group by year(Datum)<<
Diese Abfrage funktioniert wunderbar. Allerdings kommt PHP mit diesem Datumsformat nicht klar. Gibts Funktionen von MySQL oder PHP die mir das Format (JJJJ-MM-DD) wieder in einen Unix-Timestamp wandeln?

Eigentlich ist es nicht vorgesehen Datensätze von vorhergehenden Jahren zu löschen. Falls doch, so ist es jedoch nicht relevant neue Datensätze mit zuvor gelöschen Datensätzen und der dadurch entstandenen Lücke im Datenfeld 'Nummer' zu schließen.


Mir ist noch etwas anderes aufgefallen als ich mit der Funktion year() experimentiert habe: Formuliere ich eine Abfrage mit ...year(now())... so erhalte ich wie erwartet die Jahreszahl. Nachdem das Datenfeld 'Datum' im ersten Testentwurf mit Unix-Timestamps gefüllt war habe ich ...year(Datum)... ausprobiert und als Ergebnis nur NULL bekommen, warum?


MfG Manu
Tschaui

Greg G
27.02.2003, 09:23:46
Hallo Manu

Sperren:
Schau vielleicht mal in ein Buch über MySQL rein, wenn dir die Informationen zum Sperren noch nicht weiterhelfen. Wenn du echt Probleme hast, kann ich am Wochenende noch mal ein Beispiel rauskramen.

Timestamp:
Die Funktion mktime() wandelt ein Datum in einen Timestamp um.

http://www.selfphp.com/funktionsreferenz/datums_und_zeit_funktionen/mktime.php

Wenn du also einen MySQL-String hast, z.B.


$dat="2003-02-27";
$arr = explode("-", $dat);
/* $arr[0]="2003", $arr[1]=02, $arr[2]=27 */
$zeitstempel=mktime(0,0,0,$arr[1],$arr[2],$arr[0]);


siehe auch
http://www.selfphp.info/forum/forum/showthread.php?s=&threadid=3465

Ich weiß aber nicht, ob du das wirklich brauchst. Wenn du damit besser umgehen kannst, dann nutze es ruhig.

Löschen:
Das Problem beim Löschen ist nicht das Auffüllen der Lücken, im Gegenteil: Das sollte unbedingt vermieden werden. Wenn du früher einen Datensatz 2002-133 hattest, der gelöscht wird, und du hast in einer anderen Tabelle einen Datensatz, der sich auf 2002-133 bezieht (und das wird nach meiner Erfahrung irgendwann mal gefordert sein, wenn du nicht bald wieder aus dem Laden draußen bist :-), dann ist es OK, wenn du einen Fehler ausgibst, weil 2002-133 nicht mehr existiert. Aber auf keinen Fall darf dann ein anderer späterer Datensatz diese Nummer bekommen.

Das Problem, das beim Löschen also auftreten kann, ist, dass du den letzten Datensatz eines Jahres löschst. (z.B. insert 2002-1, 2002-2, 2002-3, 2002-4 nach der (Max+1)-Regel, delete 2002-2, insert 2002-5 kein Problem, aber jetzt: delete 2002-5, insert 2002-5, wenn du dir nicht merkst, welches die letzte ID eines Jahres war.)

Null bei year():
Ich nehme an, dass das daran liegt, dass du den TimeStamp in der Datenbank in einem Int Unsigned-Feld abgelegt hast. year(Int) in der MySQL-Abfrage ist dann wohl NULL.
Es gibt auch TimeStamp-Felder, damit könnte der Zugriff mit year() klappen. Aber die würde ich nur benutzen, um den Zeitpunkt des Erstellens eines Datensatzes, oder der Änderung festzuhalten. Vielleicht noch, um andere Vorgänge zu loggen, wie z.B. eine Benutzer-Anmeldung.
Wenn du Daten hast, die häufig ausgegeben werden, nimm die Datumsfelder.

GG

BlindCrazyBoy
28.02.2003, 00:31:13
Hiho Greg,

mensch so langsam nimmt die Sache hier Form an ;-) Hab heute den Benutzerkonten-Bereich in der Datenbank angelegt und bin wieder auf eine Problematik gestoßen :-(
Der Benutzername soll maximal 10 Stellen, Zeichen von a-z, A-Z, 0-9 und _ haben. Sonderzeichen und der Rest sind nicht erwünscht. Gibts schon fertige Funktionen, die mir den username überprüfen oder muss ich mir selbst eine Überprüfung aus verschiedensten String-Funktionen basteln? ;-)

Hmhm, also das MySQL Buch, welches mir mein Betrieb zur Verfügung stellt ist nicht der Hit. Vom Sperren von Tabellen ist da nichts zu finden. Lediglich ein bißchen Grundlagen, Installation und Wartung eines MySQL-Servers :-(

Die Sache mit der explode-Funktion und dem DATE-Datenfeld ist eine tolle Sache, ein bißchen mehr Aufwand ist es mit dem DATETIME-Datenfeld aber ich habs hinbekommen ;-)

Noch was zu Datenfeldern und dem Attribut 'NOT NULL': Ist es zulässig in einem INT NOT NULL AUTO_INCREMENT PRIMARY KEY Datenfeld den ersten Eintrag in die Tabelle mit dem Wert 0 zu setzen?

Also ich kann schon mal beruhigt sagen, solange ich noch in dem Betrieb bin wird es keine Tabellenbezüge geben :o) Lediglich das Löschen des letzten Datensatzes stellt ein Problem dar, das mir bis heute noch garnicht bewußt war :-(

Aber ich denke mal, dass wir das schon noch irgendwie hinkriegen werden, oda?


MfG Manu
Tschaui

Greg G
28.02.2003, 08:28:26
Servus Blinder Verrückter

Zum Benutzernamen:
Die Lösung lautet: Reguläre Ausdrücke.

if (ereg("^[_a-zA-Z0-9]{6,10}$",$name)){
&nbsp;&nbsp;echo "Name OK.";
}

Sollte nur die gewünschten Namen mit der Länge 6-10 zulassen. Hab es aber nicht getestet und bin selbst noch nicht so fit in R.A. in PHP.

http://www.phpbuilder.com/columns/dario19990616.php3

Sperren:
Ich schau mal nach einem LOCK-Beispiel.

Auto-Increment.
Meinst du, wenn du eine leere Tabelle hast, die 0 statt der 1 zu benutzen? Wozu? Generell (nicht Auto-Increment) gilt aber NULL ungleich 0.

Sekundärschlüssel und Löschen:
OK, ich habe da auch etwas schwarz gemalt, denn die Tabellenbezüge würdest du über den Primärschlüssel (dein Auto-Inkrement ID-Feld) modellieren. Dennoch ist es keine gute Idee, schon einmal vergebene Jahres-Nummern nach dem Löschen wieder zu vergeben.
Die Lösung dazu lautet einfach: Speichere für jedes Jahr die zuletzt vergebene Nummer in einer einfachen Tabelle:

Jahr | LastID
------------------
2001 | 132
2002 | 217
2003 | 23

GG

BlindCrazyBoy
01.03.2003, 13:38:27
Hallöle Greg,

also mit 'blind' haste garnet mal so unrecht... Wie's mit 'verrückt' aussieht müssen andere beurteilen ;-)

Reguläre Ausdrücke:
Dein Beispiel mit den regulären Ausdrücken hat wunderbar geklappt. Habe es noch ein wenig umgeändert und so können nun auch die eMail-Adressen geprüft werden (in der users-Tabelle)

Sperren:
Ein Beispiel dafür wäre klasse, denn wie schon gesagt das MySQL-Buch hüllt sich über dieses Theman in schweigen ;-)

Zweite Tabelle:
Ist auf jeden Fall besser, wenn Lücken nicht aufgefüllt werden. Auch um eine neue laufende Nummer zu ermitteln ist das mit der zweiten Tabelle eine einfachere Lösung

'Seitenumbrüche':
Gestern ist mir eine weitere Vorgabe auf's Auge gedrückt worden: Falls bei einer Abfrage mehr als X Datensätze ermittelt werden, so sollen diese auf mehreren Seiten verteilt werden. Leider kenne ich den Fachausdruck (falls es dafür einen gibt :-) dafür nicht. Ich hoffe Du weißt auf was ich hinaus will *g*


Schönes (Faschings)wochenende...
MfG Manu
Tschaui

Greg G
01.03.2003, 22:09:55
Hellau

Sperren-Beispiel


$res = mysql_query("lock tables BriefTabelle write, MaxMerkerTabelle write");
if (!$res)
{
die ("Fehler beim Sperren");
}


$Query='select ........ from MaxMerkerTabelle where ... '; //MaxWert holen.
$res = mysql_query($Query)
or die("Abfrage $Query konnte nicht ausgef&uuml;hrt werden.");

//Max auswerten, bzw. falls das Jahr noch nicht da war auf 1 setzen.
$max=$max+1;
$Query="insert into BriefTabelle ...."; //Neuer Datensatz
$res = mysql_query($Query)
or die("Abfrage $Query konnte nicht ausgef&uuml;hrt werden.");

if ($max>1){

$Query="update MaxMerkerTabelle ...."; //In der Tabelle, die den Max-Zähler bestimmt
$res = mysql_query($Query)
or die("Abfrage $Query konnte nicht ausgef&uuml;hrt werden.");
} else {
$Query="insert into MaxMerkerTabelle ...."; //In der Tabelle, die den Max-Zähler bestimmt
$res = mysql_query($Query)
or die("Abfrage $Query konnte nicht ausgef&uuml;hrt werden.");
}
$res = mysql_query("unlock tables");



Wichtig ist, dass alle Tabellen, die benutzt werden gesperrt werden, und zwar auch jeder Alias-Name. Wenn du eine Abfrage mit "select ........ from MaxMerkerTabelle MMT where ..."
benutzt, dann musst du auch "lock tables BriefTabelle write, MaxMerkerTabelle write, MaxMerkerTabelle MMT write" benutzen.

Ich habe (unter anderen) das Buch MySQL im Einsatz von H.G Raymans, auch nicht gerade die Erfüllung, aber es steht das meiste was man so braucht drin.

Seitenumbrüche

Ich nehme an, da suchst du die limit Option von MySQL

http://www.f078.net/php/tutorial/php/blaettern.php
Hab's mir selbst nicht genau angesehen, aber ich denke, da wirst du etwas mit anfangen können.

BlindCrazyBoy
16.03.2003, 22:46:30
Hiho Greg,

tschuldige die Verspätung aber ich hatte Urlaub.. ;-) Habe das mit dem Sperren der Tabellen mal ausprobiert und mir ein paar Gedanken darüber gemacht...

Die Erfassung der Daten erfolgt in drei (3) Schritten:

1. Eingabe der Daten in ein HTML Formular
2. Nochmalige "Vorschau" der Daten <<hier wird auch die laufende Nummer ermittelt, angezeigt und gleichzeitig die Tabelle gesperrt
3. Der Datensatz wird eingetragen <<Tabelle soll wieder entsperrt werden

Meine Überlegungen:

- Windows Clients haben die Eigenschaft, dass sie des öfteren abstürzen.. passiert das in Schritt 2 so bleibt die Tabelle weiter gesperrt?!
- Bleibt die Sperrung der Tabelle aufrecht erhalten, wenn ich zwischen zwei Skripten wechsle (also von Schritt 2 auf 3) ?
- Führt in der Zeit, in der die Tabelle gesperrt ist, ein anderer Client eineAbfrage durch, so tut sich auf dem Bildschirm des anfragenden Clients nichts, bis die Sperre aufgehoben wurde - dann erhält der Client eine Fehlermeldung: (Fatal Error: Maximum execution time of 30 seconds exceeded) Hier greift auch kein or die()

Was kann man gegen diese unerwünschten Ereignise tun?


MfG Manu
Tschaui

Greg G
22.03.2003, 20:05:53
Nach meiner Kenntnis, aber hier kann ich mich irren, ist die Sperrung nur während des Ablaufs EINES Skripts aktiv. Der PHP Interpreter beendet ja auch (zum Glück) die Verbindung zum MySQL-Server von selbst, wenn das Skript zu Ende ist.

Deshalb mein Vorschlag:

Eingabeformular

Vorschauformular mit Option Ändern oder Speichern

Speichern-Formular, dieses sperrt die Tabellen, besorgt die Nummer, trägt ein und entsperrt.
Danach sollte das Formular vielleicht auf anderes weiterleiten, weil du sonst die Reload-Problematik hast.
(So wie es hier im Forum auch gelöst ist.)
Es gibt für diese Problem auch eine andere Möglichkeit. Wenn's dich interessiert, schick mir am besten noch eine PM, weil ich wegen einer Terminarbeit erst mal nicht mehr ins Forum schauen werde.

Edit: Sehe gerade, dass du zu einer verwandten Problematik noch einen Thread laufen hast. Die Antwort von c4 kannst du natürlich auch einbauen.

GG