Große html Tabelle - Ladezeit optimieren

djjlx

---???---
ID: 62937
L
9 Mai 2006
599
21
Hallo!

Nun erstmal habe ich es endlich geschafft einen Arbeitszeitplan funktionieren zu erstellen.:biggrin:
Ansich funktioniert alles einwandfrei, nur jetzt im laufenden Betrieb fällt mir auf das die Ladezeit immer langsamer wird.

Dazu hab ich mal die Ladezeit gemessen. Für die Durchrechnung braucht der Server lediglich 0.6 Sekunden od. Mircosekunden weiß nicht mehr so genau. Bis die Anzeige komplett geladen ist jedoch ca. 30-40 Sekunden.

Zur Größe des Plans:
Normaler html table mit 30 bzw 31 Spalten für die Tage und ca. 300 Zeilen für die Mitarbeiter.
Die Zeilen werden in einer Schleife durchlaufen mit Einsatzort / Tag ausgegeben.

Mein erster Gedankenansatz ist den html Table an Hand einer funktion zu erstellen.
Nur bevor ich mir das Antue, würde das die Clietladezeit verbessern?

Bzw. was könnte man optimieren um diese Ladezeit zu verbessern. Bin in dieser Thematik nicht so gut bewandert. Klar es kommt auch immer auf die Internet geschwindigkeit des Users an.

Es werden auch kleine Bilder ausgegebn für Krank und Urlaub. Immer das gleiche ungefär 2kb groß.

Was meinen die Experten wo wäre ein Anfangspunkt?
- Die Bilder verkleinern bzw. eventuell komplett raus.
- Aufbau der Tabelle, so dass die ganze php Datei kleiner wird.

Wäre über ein paar Tipps sehr dankbar bzw. wie würdet ihr das ganz aufbauen in groben Zügen.

Gefordet ist:
Kalender im Tabellenkopf Tageweise
Für jeden Mitarbeiter eine Zeile und in den Tagesspalten den Einsatzort anzeigen. Wenn Krank oder Urlabu dann eben das anzeigen.

Als Beispiel:
Code:
<table>
<tr>
 <td colspan='8'>KW1</td>
</tr>
<tr> 
 <td> </td>
 <td>1.1</td>
 <td>2.1</td>
 <td>3.1</td>
 <td>4.1</td>
 <td>5.1</td>
 <td>6.1</td>
 <td>7.1</td>
</tr>
<tr>
 <td>Max Müller</td>
 <td>Einsatz</td>
 <td>Einsatz</td>
 <td>Einsatz</td>
 <td>Einsatz</td>
 <td>Einsatz</td>
 <td>Einsatz</td>
 <td>Einsatz</td>
</tr>
</table>

Gruss
djjlx
 
Wenn es nur ein Bild gibt und dieses lediglich 2kb verbraucht, dann liegt die Ladezeit nicht daran. Schließlich wird das Bild nur 1x geladen und danach aus dem Cache geladen.

Das liegt eher an der Schleife, die für jeden Mitarbeiter ausgeführt werden muss.

Ich nehme mal an, dass du die Schleife bei 300 Mitarbeiter und 31 Tagen genau 9300x durchlaufen lässt? Oder hast du das anderes gelöst?

Wenn ja, dann kannst du dir ja denken, warum das so lange dauert. Optimieren ist das angesagt!

Ich weiß auch nicht, ob du immer schon den kompletten Monat darstellen musst. Vielleicht reicht ja auch die aktuelle Woche. Das wären bei der oben genannten Programmierung zwar immernoch 2100 Schleifendurchläufe, aber eben schonmal eine ganze Menge weniger.

Sicherlich gibt es auch Möglichkeiten, die Programmierung ganz anders zu gestalten.

Da ich hier gerade nur spekuliere, wäre es wohl sinnvoll, wenn du erstmal was zu deinem Code sagen würdest.
 
Hallo!

Danke für die rasche Antwort!

Ja im grundgenommen hast du Recht. Ich hab 2 Tabellen in der Db im Hintergrund!
Die erste ist die Personaltabelle die zweite ist die Planung.

Die Schleife sucht dann anhand der Personal Id in der Planung pro Tag einen Datensatz, wenn vorhanden dann ausgeben ansonst leer.

Ich habe vor paar Tagen in einer Suchmaschine ein kleines script für die Ladezeit gefunden. Da habe ich in der ersten Zeile der PHP Datei mircotime und in der letzten microtime gesetz um die Ladezeit zu ermittel da dort eben nur 0.6 herausgekommen ist dachte ich das die Scriptladezeit eigentlich vertretbar ist aber die "Anzeigezeit" eben.

Ja Grundsätzlich muss das ganze Monat angezeit werden.

Gruss
djjlx
 
Zuletzt bearbeitet:
Größere Tabellen brauchen gerne mal etwas Zeit, bis sie vollständig gerendert sind. Du kannst den Browser dabei eigentlich nur unterstützen - was er letzlich draus macht, kann man immer nur schwer absehen.

Als allererstes würde ich der Tabelle anhand von <colgroup> und <col> feste Spaltenbreiten vorgeben. Ansonsten versucht der Renderer die ganze Zeit, die optimale Spaltenbreite selbst zu bestimmen, was durchaus kosten kann.

Dann noch ein paar Hinweise am Rande:

- Versuch, so viel überflüssige Tags und Whitespace wie möglich einzusparen. Je weniger beim Renderer ankommt, desto schneller geht's (ist zugegeben nur'n Tropfen auf dem heissen Stein, aber eventuell ist es ja doch 'ne merkliche Besserung).
- Keine impliziten Styles und vor allem: Keine via <link> eingebundenen Stylesheets im <body>. Der Renderer muss früh wissen, was wie auszusehen hat, dann kann er auch zügig starten und blockiert nicht das restliche Rendering.
- Läuft auf der Tabelle noch irgendein JS? Das kann mitunter mehr kosten und länger dauern als alles andere...
 
Hallo!

Danke für deine Tipps!

Werd gleich mal aufräumen gehen.

Zu Js ja ein jquery overlay wenn der einsatzort angeklickt wird kommt das zum einsatz davor ist kein Js. Hoffe ja doch das es daruch nicht verlangsamt.

Die Schleife in Kompination mit dem Renderer wird wahrscheinlich auch seinen Beitrag leisten oder?

gruss
djjlx
 
Falls Du das JS zeigen kannst, mach mal. Das kann durchaus auch der Verursacher sein - Tabellen sind halt etwas verschachtelter und da kann das Durchhangeln durch den DOM schon zeitintensiv werden.

Und welche Schleife meinst Du? Die auf Serverseite, die in 0.6s abgehandelt ist? Nach diesen 0.6s liegt eigentlich alles beim Browser und solange Du die Daten nicht mit 'nem Delay über die Leitung schiebst, dürfte das eigentlich nicht die Ursache des langsamen Verhaltens sein...
 
Hallo!

Anbei mal auszüge des codes

Head (Davor ist nur die Session abfrage ob man eingeloggt ist)
HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="https://cdn.jquerytools.org/1.2.5/full/jquery.tools.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/plan.css"/>
<link rel="stylesheet" type="text/css" href="css/apple_effect_overlay.css"/>
<style type="text/css">
...
</style>
<style type="text/css" media="print">body {overflow:visible!important;}</style>
</head>

dann der Bodyteil:
PHP:
<table>...

//SCHLEIFE
for ($i = 1; $i <= date("t",mktime(0,0,0,$month,1,$year)); $i++)
{
    $tag = "$i.$month.$year";  
    $daystart = mktime(0,0,0,$month,$i,$year);
    $dayend = mktime(23,59,59,$month,$i,$year); 
    $int_pln_qry = db_query("SELECT `id`,... FROM `hier Plan tab` WHERE `Personal ID` = '%s' AND `start` BETWEEN '%s' AND '%s'",$data["pid"],$daystart,$dayend);
//Dann die Abfragen Krank Urlaub usw.
<td>...

und der Schluss mit JS:
PHP:
<!-- overlayed element -->
<div class="apple_overlay" id="overlay">
	<div class="contentWrap"></div>
</div>
<script>

$(function() {

	// if the function argument is given to overlay,
	// it is assumed to be the onBeforeLoad event listener
	$("a[rel]").overlay({


		effect: 'apple',

		onBeforeLoad: function() {

			// grab wrapper element inside content
			var wrap = this.getOverlay().find(".contentWrap");

			// load the page specified in the trigger
			wrap.load(this.getTrigger().attr("href"));
		}


	});
});
</script>
</body>
</html>

Das jquery ist halt ein Plugin

Hoffe das ist das was du gemeint hast!

Gruss
djjlx
 
Dein Performance-Problem steckt in deiner Schleife, in jeder Iteration wird eine Datenbankabfrage gemacht, klar dass das ewig dauert.

Als Faustregel: Wenn du Datenbankabfragen in einer Schleife hast, wurde meist etwas falsch designt.
 
Halloa,

die DB-Abfrage in der Schleife ist sicherlich nicht sehr performant (hier sollte eigentlich ein einfacher Join helfen), aber da die Verarbeitungszeit des Scripts hier mit 0,6 Sekunden (vermute ich mal) angegeben wurde, nicht der eigentliche Flaschenhals.

Zunächst eine Frage: Wie groß ist die ausgelieferte Datei?

Wenn du hier nichts einsparen kansst, dann versuche einmal alle Zeilen der Tabelle als einzelne Tabelle anzuzeigen (Breite jeder Spalte explizit angeben, damit weiterhin der Eindruck einer Tabelle entsteht). Dies vergrößert die Gesamtgröße der Datei, aber der Browser kann jede einzelne Tabelle sofort ausgeben und muß nicht warten, bis die gesamte große Tabelle geladen und analysiert wurde.

Die Anzeige der Grafiken kann auch ein Grund für den Geschwindigkeitsabfall sein, je nach Cacheverhalten des Browsers wird für jede Grafik eine Anfrage an den Server gestellt, ob diese sich geändert hat, bevor die Grafik aus dem Cache des Browsers genommen wird. Hier hilft es, die Grafik in die css-Datei einzubinden - nicht als Link, sondern direkt als Binärdaten (wie das geht, weiß ich aber nicht mehr), so dass keine Anfrage an den Server für die Grafik gestellt wird.

Gib mal eine Rückmeldung, das Thema find ich interessant.

JOIN SQL (schnell mal zur Info):

SELECT t1.feld1, t2.feld1, ... from tabelle1 as t1 LEFT JOIN tabelle2 as t2 ON t1.idfeld1 = t2.idfeld2 WHERE bedingung ...

Viele Grüße aus Berlin

leller
 
Hier hilft es, die Grafik in die css-Datei einzubinden - nicht als Link, sondern direkt als Binärdaten (wie das geht, weiß ich aber nicht mehr), so dass keine Anfrage an den Server für die Grafik gestellt wird.

Also sowas habe ich noch nie gehört, liegt wahrscheinlich daran dass soetwas nicht geht. Wäre sehr interessiert ob du das irgendwie belegen kannst ;)
 
Stichwort: Data URIs - werden nur leider nicht von allen Browsern unterstützt.

@djjlx: Wie sieht's denn momentan aus? Schon irgendwas erreichen oder den Flaschenhals genauer einkreisen können?
 
Stichwort: Data URIs - werden nur leider nicht von allen Browsern unterstützt.

Da hab ich doch glatt dazugelernt, dass dies auch direkt im HTML-Tag z.b: als scr-Attribut im image geht.

Die Frage bleibt aber wirklich, ob die fehlende Unterstützung des IE7 und älter den Einsatz dieser Technik nicht ausschließt.

Viele Grüße aus Berlin

leller
 
Halloa,

eine Rückmeldung wäre echt nett, welcher Ansatz deutlichen Performance-Gewinn gebracht hat. Also deswegen mal schieben.

Danke im Voraus mit Grüßen aus Berlin

leller
 
Hallo!

Sorry das ich erst jetzt antworte!

Ich habe als erstes mal colgroup hinzugefügt. bei den ersten versuchen kommt es mir schneller vor muss ich aber weiter beobachten.

Danach hab ich mir das Thema JOIN zu Gemüte geführt und herumgespielt nach dem Motto "learning by doing".

Erste Erkenntnisse:

Mit einer Left Join Abfrage kann ich nur arbeiten wenn keine WHERE Elemet in der Abfrage sind, da sonst nur Datensätze ausgegeben werden die Zutreffend sind und sich somit der "Kalendertag" verschiebt.

Derzeit frage ich ja zuerst ob ein datensatz vorhanden
:?: JA weiter if abfragen Einsatz oder Urlaub...
:?: Nein leere Zelle
:arrow: nächste Spalte selbes Spiel

Was vielleicht weiter oben nicht so zur Geltung kommt:
Ich hol mir die Personal Id aus der Personal tabelle
damit frage ich dann tageweise (WHERE `begin der schicht` BETWEEN `Tag 0 Uhr AND Tag 23:59:59) in der Planung ab. Gibts einen Eintrag wird er ausgegeben gibts keinen leere zelle.

Diese Methode müsste ich nun in ein LEFT Join abfrage verwandeln.
:arrow: Derzeitiges Kopfzerbrechen :wall:


Ohne Where Klausel bekomm ich dann alle Datensätze ausgegeben.

Ich bin derzeit dabei die Abfragen so auszugeben das sich der Kalender nicht verschiebt und an Tagen wo kein Einsatz für den Mitarbeiter geplant ist nur die Zelle weiß erscheint.

Zugegeben die Join Sache wird sicherlich der richtige Weg sein nur bin ich mit dieser Abfragevariant nicht wirklich vertaut. Bzw. Muss ja dann die Ausgabe trotzallem in einer schleife passieren um immer die nächste Zeile der DB auszugeben oder sehe ich das falsch?

Gruss
djjlx
 
Zuletzt bearbeitet:
PHP:
<table>...

//SCHLEIFE
for ($i = 1; $i <= date("t",mktime(0,0,0,$month,1,$year)); $i++)
{
    $tag = "$i.$month.$year";  
    $daystart = mktime(0,0,0,$month,$i,$year);
    $dayend = mktime(23,59,59,$month,$i,$year); 
    $int_pln_qry = db_query("SELECT `id`,... FROM `hier Plan tab` WHERE `Personal ID` = '%s' AND `start` BETWEEN '%s' AND '%s'",$data["pid"],$daystart,$dayend);
//Dann die Abfragen Krank Urlaub usw.
<td>...

Das dies übel ist weisst du ja bereits :)
Bei 300 Leuten und 30 Tagen, haust du hier 9000 Abfragen durch.
zzgl deinen Sachen wie Krank Urlaub usw.

Ich kenn jetzt dein Datenbankdesign nicht, aber 2 Tabellen scheinen auch etwas wenig zu sein. Stichwort Normalform

Lösungsansatz:
Alle Daten via JOIN laden (Datum Gruppieren (DATE_FORMAT), PersonalID usw)
Und dann ausgeben. Entweder kommst du mit einer großen Abfrage oder mit einer etwas größeren sowie einer kleineren performanter ans Ziel. Musst du testen.

Was Sinn machen könnte:
1 Abfrage für die Personalsachen -> ab ins Array (auf die Größe aufpassen)
2 Abfrage für die Tage und Status an dem Tag -> ins gleiche Array

Ausgeben.
Könnte dann so aussehen:

PHP:
$arr[$persID][$day]; //status krank etc

//oder auch
$arr[$day][$persID]; //status krank etc

Das ganze mit While und List each etc durchgehen und ausgeben.
 
Zuletzt bearbeitet:
Hallo!

Zum Datenbankdesign
Ich hab 3 Tabellen
1 Planungstabelle
Hier sind nur die Planungsdaten also
ID = Planungsid|Personal Id|baustellen Id|Std. = Einsatzstunden| start des einsatzes timestamp | ende des einsatzes timestamp| inaktiv = 0 Nein, 1= Urlaub, 2= Krank usw.

2 Personal Tabelle
Personal Id|Name| geb.dat| usw. Personenbezogene Dinge

3 Baustellen Tabelle
Baustellen ID|Einsatzname|Ort| und weitere Bestell bzw Baustellendaten.

Ich werd deine Tipps mal ausprobieren und schauen ob ich ans Ziel komm da ja das Join meinen Horizont erst erweitern muss :mrgreen:

Lg
 
Hallo!

Ich hab es nun geschafft mir ein query zu bauen mit left join das mir alle Datensätze ausgibt die in einen Monat vorhanden sind.
Dementsprechnend bekomm ich natürlich 1 Mitarbeiter mehrmals ausgegeben wenn er mehrere Einsätze hat.

Kann ich in einer while Schleife
PHP:
while($data = mysql_fetch_array($res))
den vorherigen Datensatz ansprechen?

Ich würde jetzt gerne prüfen ob die Personal ID die gleiche ist als die im vorherigen Datensatz um zu steuern ob eine neue Zelle oder eine neue Zeile in der html Tabelle angezeigt werden muss.

Ich habs mit $data["pid"][-1] probiert da kommt aber leider keine ausgabe.

Mein Testcode derzeit um zu sehen was ausgegben wird ist
PHP:
echo"Name jetzt: ".$data["name"]." - Name minus 1: ".$data["name"][-1]."<br>";

//AUSGABE:
Name jetzt: Huber - Name minus 1: 
Name jetzt: Huber - Name minus 1: 
Name jetzt: Huber - Name minus 1: 
Name jetzt: Mayer - Name minus 1: 
Name jetzt: Mayer - Name minus 1: 
Name jetzt: Mayer - Name minus 1: 
Name jetzt: Mayer - Name minus 1: 
usw.

//Zu erreichende Ausgabe:
Name jetzt: Huber - Name minus 1: 
Name jetzt: Huber - Name minus 1: Huber 
Name jetzt: Huber - Name minus 1: Huber 
Name jetzt: Mayer - Name minus 1: Huber 
Name jetzt: Mayer - Name minus 1: Mayer 
Name jetzt: Mayer - Name minus 1: Mayer 
Name jetzt: Mayer - Name minus 1: Mayer


Gruss djjlx
 
Schreib die vorherige Personal ID doch einfach in eine "temporäre Variable" :)
PHP:
$tmpPid = -1;
while($data = mysql_fetch_array($res))
{
   if($tmpPid==$data['pid']) {
      //Mache das Eine
   } else {
      //Mache das Andere
   }

   [...]

   $tmpPid = $data['pid'];
}
unset($tmpPid);
 
Das geht natürlich nicht, $data enthält nur die aktuelle Zeile.
Außerdem fängt ein Array "immer" bei 0 an und hat kein Negativen Indizes.

*edit*
zu langsam
 
Schreib die vorherige Personal ID doch einfach in eine "temporäre Variable" :)
PHP:
$tmpPid = -1;
while($data = mysql_fetch_array($res))
{
   if($tmpPid==$data['pid']) {
      //Mache das Eine
   } else {
      //Mache das Andere
   }

   [...]

   $tmpPid = $data['pid'];
}
unset($tmpPid);

Ja das würde nur funktionieren wenn die Id fortlaufend ist oder?

Die Abfrage $res liefert nur Daten die in diesem Monat eingeplant sind.
Wenn jetzt die Personal ID 2 keinen Eintrag hat wird sie auch nicht ausgegeben.

Gruss
djjlx