Vinzenz Mai: Geschwindigkeit und Performance PHP - MySQL

Beitrag lesen

Hallo Fabienne,

[...]typisches Anfängerbeispiel: DB-Funktionalität durch PHP-Skript nachgeahmt
Jeder muss mal anfangen und dazulernen....

das war kein Vorwurf, der auf Dich gemünzt war.
Im Gegensatz zu Dir wollen viele gar nicht dazulernen und glauben, dass ihr
PHP-Code völlig richtig und angemessen sei.

b) den Gesamtumsatz aus dem aktuellen Jahr
   ist das die Summe der Einträge oder der zeitlich letzte Eintrag im Jahr
zeitlich der letzte

Wer lesen kann, ist klar im Vorteil. Beim aufmerksamen Lesen Deines Postings
habe ich das auch feststellen können.

Minutenbereich kommen dürfte (vernünftige Indizierung vorausgesetzt).
Gerade mal nachgemessen: Pro Datensatz zwischen 0,5 und 0,9 Sekunden!!!

Das ist verdammt viel, zu viel.

db.kunden
ID | Kundennummer | Name
1  | 12345        | Müller GmbH
2  | 932749       | Moritz AG

db.umsatz
ID | kunden_id | Umsatz  | Jahr | erstellt_am
15 | 1         | 1789.12 | 2007 | 2007-01-05
16 | 1         | 1812.15 | 2007 | 2007-01-08
17 | 2         | 66.09   | 2007 | 2007-01-05
18 | 1         | 89.99   | 2008 | 2008-01-16

db.kontakte
ID | kunden_id | Name | Vorname   | key
1  | 1         | Meier   | Fritz  | no
2  | 1         | Metzger | Anton  | yes
3  | 2         | Kohl    | Helmut | yes

Ergebnis sollte sein:
Firma       ; 2007    ; 2008  ; Key-Manager

Müller Gmbh ; 1812.15 ; 89.99 ; Metzger
Moritz AG   ;   66.09 ;       ; Kohl

OK, der Reihe nach:

Ein paar Anmerkungen zu Feldnamen:
Reservierte Worte wie "key" zu verwenden, ist keine gute Idee.
Es ist bei generiertem Code immer eine gute Idee, sicherheitshalber alle
Namen von Tabellen und Spalten zu maskieren. Bei MySQL ist der Backtick das
Maskierungszeichen.
Punkte in Spaltennamen zu verwenden, ist eine ganz extrem schlechte Idee.
Die ist noch schlechter als die Verwendung von reservierten Worten. Punkte
trennen bei den diversen SQL-Dialekten Datenbanken, Schemata, Tabellen und
Spaltennamen bei vollqualifizierten Namen.

1. Schritt: Ermittle die Key-Manager der Kunden:

  
SELECT  
    kunden.Name AS Firma,            -- mit kann man freundliche Namen vergeben  
    kontakte.`key` AS `Key-Manager`  -- key ist ein Schlüsselwort und muss daher  
                                     -- maskiert werden. Minuszeichen sind auch  
                                     -- nicht gut :-)  
FROM  
    kunden  
LEFT JOIN                            -- Wir nehmen auch Firmen mit, die uns  
    kontakte                         -- keinen Key-Manager genannt haben  
ON  
    kunden.id = kontakte.id_kunden  

2. Schritt: Ermittle den Umsatz der Kunden im Jahr 2007:
Das erfordert eine korrelierte Unterabfrage, siehe dazu z.B. dieses Archivposting:

  
SELECT                               -- Gib mir  
    u.kunden_id,                     -- die id des Kunden  
    u.umsatz                         -- und seinen Umsatz  
FROM                                 -- aus der Tabelle  
    umsatz u                         -- umsatz, die ich über das Alias u anspreche  
WHERE                                -- wobei nur der Umsatz angezeigt wird,  
    u.erstellt_am = (                -- bei dem das Erstellungsdatum  
        SELECT                       --  
            MAX(um.erstellt_am)      -- das maximale und somit neueste Datum  
        FROM  
            umsatz um                -- in der Tabelle umsatz, die hier über  
                                     -- um angesprochen wird  
        WHERE                        -- für  
            um.kunden_id = u.kunden_id  -- jede kunden_id  
            AND Jahr = 2007             -- und das Jahr 2007  
     )  

3. Der Umsatz der Kunden für das Jahr 2008 kann analog ermittelt werden.

4. Nun bauen wir die Jahresumsätze ein.
   Durch einen LEFT JOIN auf die Jahresumsätze wird berücksichtigt, dass auch
   Kundendaten angezeigt werden, von Kunden, die in wenigstens einem der Jahre
   keinen Umsatz gemacht haben (z.B. Neukunden)

  
SELECT  
    kunden.Name AS Firma,  
    u2007.umsatz as `Umsatz 2007`,   -- Aussagekräftige Spaltenüberschriften,  
    u2008.umsatz as `Umsatz 2008`,   -- die Maskierung erfordern  
    kontakte.`key` AS `Key-Manager`  
FROM  
    kunden  
LEFT JOIN                            -- Wir nehmen auch Firmen mit, die uns  
    kontakte                         -- keinen Key-Manager genannt haben  
ON  
    kunden.id = kontakte.id_kunden  
LEFT JOIN (                          -- will ich Daten aus einem Subselect,  
    SELECT                           -- das ich als "Tabelle" anspreche, so  
        u.kunden_id,  
        u.umsatz  
    FROM  
        umsatz u  
    WHERE  
        u.erstellt_am = (  
            SELECT  
                MAX(um.erstellt_am)  
            FROM  
                umsatz um  
            WHERE  
                um.kunden_id = u.kunden_id  
                AND Jahr = '2007'  
        )  
) u2007                          -- muss ich dafür Namen vergeben.  
ON kunden.id = u2007.kunden_id  
  
LEFT JOIN (  
    SELECT  
        u.kunden_id,  
        u.umsatz  
    FROM  
        umsatz u  
    WHERE  
        u.erstellt_am = (  
            SELECT  
                MAX(um.erstellt_am)  
            FROM  
                umsatz um  
            WHERE  
                um.kunden_id = u.kunden_id  
                AND Jahr = '2008'        -- In MySQL kann und sollte man auch Zahlen  
                                         -- als Zeichenketten übergeben  
        )  
) u2008  
ON kunden.id = u2008.kunden_id  

sollte das gewünschte Ergebnis liefern, MySQL 4.1.x vorausgesetzt.
Getestet (mit anderen Daten) mit MySQL 5.0.45

Für die Performance wichtig sind Indexe für die Spalten kunden_id in den
Tabellen umsatz und kontakte.

Anschließend kannst Du die Daten mit PHP wie gewohnt ausgeben.

Freundliche Grüße

Vinzenz