phpbar.de logo

Mailinglisten-Archive

Re: COUNT
Archiv Mailingliste mysql-de

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: COUNT



Frank Oellien schrieb am Samstag, den  9. Oktober 1999:
> I habe folgendes Problem. Ich habe eine Tabelle, die folgendermassen
> aufgebaut ist:
> 
> NSCNr | CellID | Konz.
>  123  |   1    |  2
>  123  |   2    |  2
>  123  |   3    |  2
>  123  |   1    |  2.5
>  123  |   2    |  2.5
>  124  |   1    |  2
>  124  |   3    |  3
>  125  |   1    |  1.6
>  125  |   2    |  1.5
>  126  |   2    |  1.5
>  126  |   4    |  1.5
> 
> Als Ergebnis moechte ich gerne die Anzahl aller verschiedenen (!)
> NSCNr fuer eine CellID haben:
> 
> CellID | count(NSCNr)
>   1    |     3
>   2    |     3
>   3    |     2
>   4    |     1
> 
> Der Befehl:
> select CellID, count(NSCNr) from tgi group by CellID;
> fuehrt allerdings nur zum Zaehlen aller NSCNr (was ja auch korrekt
> ist aber nicht meinem Wunsch entspricht):
> 
> CellID | count(NSCNr)
>   1    |     4
>   2    |     4
>   3    |     2
>   4    |     1
> 
> Wie muss die Anfrage richtig lauten?

Dafür gäbe es in SQL die COUNT-Variante 'COUNT(DISTINCT ...)', mit der
Deine Abfrage so aussehen würde:
  SELECT CellID, COUNT(DISTINCT NSCNr) FROM tgi GROUP BY CellID;

Leider unterstützt MySQL dieses COUNT(DISTINCT ...) aber erst ab
Version 3.23.x!


Falls Du eine ältere MySQL-Version benutzt, kommt man nur auf Umwegen
an das gewünschte Ergebnis:

- Selber auf der Anwendungsseite zählen:
    SELECT DISTINCT NSCNr, CellID FROM tgi ORDER by CellID;
  Und dann für jede CellID die Anzahl der ausgegebenen Zeilen
  mitzählen.

- Mit Hilfe einer temporären Hilfstabelle:
    CREATE TABLE tmp ( NSCNr INT, CellID INT );
    INSERT INTO tmp SELECT DISTINCT NSCNr, CellID FROM tgi;
    SELECT CellID, COUNT(NSCNr) FROM tmp GROUP BY CellID;
    DROP TABLE tmp;
  Der entscheidende Punkt ist wieder das SELECT DISTINCT, welches
  sicherstellt, daß keine Duplikate in der temporären Tabelle landen.

  Nachteil ist hier vorallem der zusätzliche Platzbedarf für die
  temporäre Tabelle.  Falls diese Abfrage mehrfach gleichzeitig laufen
  kann, muß man zudem darauf achten, daß es keine konkurrierenden
  Zugriffen auf die temporäre Tabelle geben kann - entweder durch
  Locking oder indem die temporäre Tabelle jedesmal einen anderen,
  eindeutigen Namen bekommt.  Letzteres geschieht ab MySQL V3.23.x
  automatisch, wenn man CREATE TEMPORARY TABLE nutzt.

- Mit Hilfe eines Self-Joins:
    SELECT t1.CellID
        ,  COUNT(t1.NSCNr)-COUNT(t2.NSCNr) AS cd
    FROM  tgi AS t1 LEFT JOIN
          tgi AS t2 ON t1.NSCNr=t2.NSCNr
                   AND t1.CellID=t2.CellID
                   AND t1.Konz<t2.Konz
    GROUP BY CellID;

  Die Ausgangsidee dabei ist, daß man zuviel gezählte Zeilen wieder
  abziehen will.  Welche sind das aber?  Das sind alle die Zeilen,
  wo es noch anderen Datensätze mit der gleichen (CellID, NSCNr)-
  Kombination gibt!

  Das könnte man mit einem LEFT JOIN so testen:
     
    FROM  tgi AS t1 LEFT JOIN
          tgi AS t2 ON t1.NSCNr=t2.NSCNr   /* gleiche NSCNr  */
                   AND t1.CellID=t2.CellID /* gleiche CellID */
                   AND t1.Konz!=t2.Konz    /* aber verschiedene Zeilen */

  Wenn die Tabelle einen Primärschlüssel besitzt, dann sollte man
  statt des Konz-Vergleichs die Primärschlüssel auf Verschiedenheit
  testen - Ziel ist halt sicherzustellen, daß zwei verschiedene Zeilen
  verbunden werden!

  Statt mit '!=' zu vergleiche, ist es aber geschickter, '<' zu
  verwenden.  Denn so gibt es pro (CellID, NSCNr)-Gruppe genau eine
  Ergebniszeile mit NULL-Werten auf der t2-Seite: nämlich für den
  t1-Datensatz mit der größten ID (hier: größtes Konz).

  Jetzt braucht man also nur noch die NULL-Werte in einer Spalte der
  t2-Seite zu zählen und hat die gewünschte Anzahl verschiedener
  Werte.
  Diese Zählen der Nullwerte ginge z.B. mit
     SUM( IF( ISNULL(t2.NSCNr), 1, 0 )

  Hübscher (und vermutlich schneller) ist aber, die Anzahl der
  NULL-Werte als Differenz der Gesamtanzahl=COUNT(t1.NSCNr) mit der
  Zahl der Nicht-NULL-Werte=COUNT(t2.NSCNr) zu berechnen.

Hmm, eben - kurz vorm abschicken der Mail - fiel mir noch eine
Verbesserung ein:

  Statt die NULL-Werte irgendwie kompliziert zu zählen, kann man per
  WHERE-Klausel die Ergebniszeilen mit rechtem NULL-Teil herausfiltern 
  und braucht dann nur noch ein einfaches COUNT im linken t1-Teil.

  Das hier ist also meine LEFT JOIN-Lösung:

  SELECT t1.CellID,  count(t1.NSCNr) AS NSCNr
  FROM   tgi AS t1 LEFT JOIN
         tgi AS t2 ON t1.NSCNr=t2.NSCNr
                  AND t1.CellID=t2.CellID
                  AND t1.id<t2.id
  WHERE  t2.NSCNr IS NULL
  GROUP BY CellID;

Dabei muß id eine Spalte mit Unique-Index sein oder gleich der
Primärschlüssel.
(Sonst könnte es beim '<'-Vergleich mehrere größte Elemente geben
 und es würde zu viel gezählt.)

Ciao,
  Martin
-- 
Martin Ramsch <m.ramsch_(at)_computer.org> <URL: http://home.pages.de/~ramsch/ >
PGP KeyID=0xE8EF4F75 FiPr=5244 5EF3 B0B1 3826  E4EC 8058 7B31 3AD7

---
*** Abmelden von dieser Mailingliste funktioniert per E-Mail
*** an mysql-de-request_(at)_lists.4t2.com mit Betreff/Subject: unsubscribe


Home | Main Index | Thread Index

php::bar PHP Wiki   -   Listenarchive