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