it-swarm.com.de

GROUP BY zwei verschiedene Spalten

Ich speichere Treffer. Mein Tisch sieht so aus:

    ID |   time   |   Country   
--------------------------------
    1  | 01:00:00 |    France
    2  | 01:00:00 |    France
    3  | 01:00:00 |    Brazil
    4  | 01:00:00 |    USA
    5  | 02:00:00 |    USA

Das ist meine Frage:

SELECT COUNT(*) as total_hits, HOUR(time) as hour, Country
FROM hits 
WHERE time >= CURDATE() 
GROUP BY HOUR(time)

Es zählt, wie viele Treffer ich bekommen habe und gruppiert es nach Stunden:

    time   |   total_hits   
---------------------------
  01:00:00 |    4
  02:00:00 |    1

Ich möchte ein Ergebnis, das nach der Spalte time und ein anderes nach der Spalte countries gruppiert ist. Das ist das, was ich benötige:

1) Gruppiert nach Stunden wie diesen (wie oben)

    time   |   total_hits   
---------------------------
  01:00:00 |    4
  02:00:00 |    1

2) UND gruppiert nach Ländern wie diesem

  country  |   total_hits   
---------------------------
 France    |    2
  USA      |    2
 Brazil    |    1

Ich könnte:

SELECT
       COUNT(*)
     , HOUR(time)
     , COUNT(IF( Country = 'France', Country, null)) AS France
     , COUNT(IF( Country = 'USA', Country, null)) AS USA
     , COUNT(IF( Country = 'Brazil', Country, null)) AS Brazil
FROM hits
WHERE time >= CURDATE()
GROUP BY HOUR(time)

Oder alternativ mit CASE oder SUM(Country = 'France') AS France.

In der Länderspalte gibt es jedoch mehr als nur 3 Länder. Wenn ich dies mit jedem Land tun würde, wäre meine Anfrage sehr lang.

Ich könnte das tun:

SELECT COUNT(*), Country, HOUR(time)
FROM hits 
WHERE time >= CURDATE() 
GROUP BY Country, HOUR(time)

Aber die Ausgabe wird ungefähr so ​​sein:

  time     | country   |   total_hits   
---------------------------------------
  01:00:00 |  France   |    2
  01:00:00 |  USA      |    1
  01:00:00 |  Brazil   |    1
  02:00:00 |  USA      |    1

Ich benötige jedoch eine Ausgabe wie oben erwähnt.

Im Grunde brauche ich diese Abfragen in einer Abfrage:

1) Gruppieren nach STUNDE (wie oben erwähnt)

SELECT COUNT(*) as total_hits, HOUR(time) as hour, Country
FROM hits 
WHERE time >= CURDATE() 
GROUP BY HOUR(time)

2) UND Gruppe nach Land

SELECT COUNT(*) as total_hits
FROM hits 
WHERE time >= CURDATE() 
GROUP BY Country

Leistung ist wichtig. Die Datenbank enthält Millionen von Einträgen. Vielleicht ist MySQL nicht der beste Weg für dieses Problem?

4
yoshi

Ich glaube nicht, dass ich Ihnen bei der Formatierung helfen kann, aber die Abfrage, die Sie benötigen, ist folgende:

Vorgeschlagene Abfrage

SELECT
    IFNULL(Hour,CONCAT('Total for ',IFNULL(Country,'All Countries'))) Statistic,
    COUNT(1) TotalHits
FROM
(
    SELECT Country,(time - INTERVAL MOD(UNIX_TIMESTAMP(time),3600) SECOND) Hour
    FROM hits WHERE time >= (DATE(NOW()) + INTERVAL 0 SECOND)
) AA
GROUP BY Country,Hour WITH ROLLUP;

IHRE BEISPIELDATEN

USE test
DROP TABLE IF EXISTS hits;
CREATE TABLE hits
(
    id int not null auto_increment,
    time datetime,
    country varchar(20),
    PRIMARY KEY (id)
);
INSERT INTO hits (time,Country) VALUES
('2014-06-19 01:00:00','France'),
('2014-06-19 01:00:00','Brazil'),
('2014-06-19 01:00:00','USA'),
('2014-06-19 02:00:00','USA');

IHRE PROBENDATEN GELADEN

mysql> USE test
Database changed
mysql> DROP TABLE IF EXISTS hits;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE hits
    -> (
    ->     id int not null auto_increment,
    ->     time datetime,
    ->     country varchar(20),
    ->     PRIMARY KEY (id)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO hits (time,Country) VALUES
    -> ('2014-06-19 01:00:00','France'),
    -> ('2014-06-19 01:00:00','Brazil'),
    -> ('2014-06-19 01:00:00','USA'),
    -> ('2014-06-19 02:00:00','USA');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM hits;
+----+---------------------+---------+
| id | time                | country |
+----+---------------------+---------+
|  1 | 2014-06-19 01:00:00 | France  |
|  2 | 2014-06-19 01:00:00 | Brazil  |
|  3 | 2014-06-19 01:00:00 | USA     |
|  4 | 2014-06-19 02:00:00 | USA     |
+----+---------------------+---------+
4 rows in set (0.00 sec)

mysql>

Vorgeschlagene Abfrage ausgeführt

mysql> SELECT
    ->     IFNULL(Hour,CONCAT('Total for ',IFNULL(Country,'All Countries'))) Statistic,
    ->     COUNT(1) TotalHits
    -> FROM
    -> (
    ->     SELECT Country,(time - INTERVAL MOD(UNIX_TIMESTAMP(time),3600) SECOND) Hour
    ->     FROM hits WHERE time >= (DATE(NOW()) + INTERVAL 0 SECOND)
    -> ) AA
    -> GROUP BY Country,Hour WITH ROLLUP;
+-------------------------+-----------+
| Statistic               | TotalHits |
+-------------------------+-----------+
| 2014-06-19 01:00:00     |         1 |
| Total for Brazil        |         1 |
| 2014-06-19 01:00:00     |         1 |
| Total for France        |         1 |
| 2014-06-19 01:00:00     |         1 |
| 2014-06-19 02:00:00     |         1 |
| Total for USA           |         2 |
| Total for All Countries |         4 |
+-------------------------+-----------+
8 rows in set (0.00 sec)

mysql>

Die Formatierung überlasse ich dir :-)

CAVEAT: Bitte stellen Sie sicher, dass Sie einen Zeitindex auf dem Tisch haben

ALTER TABLE hits ADD INDEX time_index (time);

Eine zusätzliche Abfrage könnte sein

SELECT
    IFNULL(Country,CONCAT('Total for ',IFNULL(Hour,DATE(NOW())))) Statistic,
    COUNT(1) TotalHits
FROM
(
    SELECT Country,(time - INTERVAL MOD(UNIX_TIMESTAMP(time),3600) SECOND) Hour
    FROM hits WHERE time >= (DATE(NOW()) + INTERVAL 0 SECOND)
) AA
GROUP BY Hour,Country WITH ROLLUP;

wessen Ausgabe wäre

+-------------------------------+-----------+
| Statistic                     | TotalHits |
+-------------------------------+-----------+
| Brazil                        |         1 |
| France                        |         1 |
| USA                           |         1 |
| Total for 2014-06-19 01:00:00 |         3 |
| USA                           |         1 |
| Total for 2014-06-19 02:00:00 |         1 |
| Total for 2014-06-19          |         4 |
+-------------------------------+-----------+
7 rows in set (0.00 sec)

UPDATE 21.06.2014 19:07 EDT

Vielleicht funktionieren die beiden mit UNION kombinierten Abfragen für Sie

SELECT
    IFNULL(Country,CONCAT('Total for ',IFNULL(Hour,DATE(NOW())))) Statistic,
    COUNT(1) TotalHits
FROM
(
    SELECT Country,(time - INTERVAL MOD(UNIX_TIMESTAMP(time),3600) SECOND) Hour
    FROM hits WHERE time >= (DATE(NOW()) + INTERVAL 0 SECOND)
) AA
GROUP BY Hour,Country
UNION
SELECT
    IFNULL(Hour,CONCAT('Total for ',IFNULL(Country,'All Countries'))) Statistic,
    COUNT(1) TotalHits
FROM
(
    SELECT Country,(time - INTERVAL MOD(UNIX_TIMESTAMP(time),3600) SECOND) Hour
    FROM hits WHERE time >= (DATE(NOW()) + INTERVAL 0 SECOND)
) AA
GROUP BY Country,Hour WITH ROLLUP;

Ich habe das WITH ROLLUP vom ersten, so dass die Summe einmal herauskommt

1
RolandoMySQLDBA