Wenn ich so einen Tisch habe:
pkey age
---- ---
1 8
2 5
3 12
4 12
5 22
Ich kann "gruppieren", um eine Zählung jedes Alters zu erhalten.
select age,count(*) n from tbl group by age;
age n
--- -
5 1
8 1
12 2
22 1
Mit welcher Abfrage kann ich nach Altersgruppen gruppieren?
age n
----- -
1-10 2
11-20 2
20+ 1
Ich habe 10gR2, aber ich würde mich auch für 11g-spezifische Ansätze interessieren.
SELECT CASE
WHEN age <= 10 THEN '1-10'
WHEN age <= 20 THEN '11-20'
ELSE '21+'
END AS age,
COUNT(*) AS n
FROM age
GROUP BY CASE
WHEN age <= 10 THEN '1-10'
WHEN age <= 20 THEN '11-20'
ELSE '21+'
END
Versuchen:
select to_char(floor(age/10) * 10) || '-'
|| to_char(ceil(age/10) * 10 - 1)) as age,
count(*) as n from tbl group by floor(age/10);
Was Sie suchen, sind im Grunde die Daten für ein Histogramm .
Sie würden das Alter (oder den Altersbereich) auf der x-Achse und den Zählwert n (oder die Häufigkeit) auf der y-Achse haben.
In der einfachsten Form könnte man einfach die Anzahl der verschiedenen Alterswerte zählen, wie Sie es bereits beschrieben haben:
SELECT age, count(*)
FROM tbl
GROUP BY age
Wenn es jedoch zu viele verschiedene Werte für die x-Achse gibt, möchten Sie möglicherweise Gruppen (oder Cluster oder Buckets) erstellen. In Ihrem Fall gruppieren Sie einen konstanten Bereich von 10.
Wir können vermeiden, für jeden Bereich eine WHEN ... THEN
-Zeile zu schreiben - es könnte Hunderte geben, wenn es nicht um Alter ginge. Stattdessen ist der Ansatz von @MatthewFlaschen aus den von @NitinMidha genannten Gründen vorzuziehen.
Lassen Sie uns nun die SQL erstellen ...
Zuerst müssen wir die Altersgruppen in 10-Gruppen unterteilen, wie folgt:
Dies kann erreicht werden, indem die Altersspalte durch 10 geteilt wird und dann der FLOOR des Ergebnisses berechnet wird:
FLOOR(age/10)
"FLOOR gibt die größte Ganzzahl gleich oder kleiner als n zurück" http://docs.Oracle.com/cd/E11882_01/server.112/e26088/functions067.htm#SQLRF00643
Dann nehmen wir die ursprüngliche SQL und ersetzen age durch diesen Ausdruck:
SELECT FLOOR(age/10), count(*)
FROM tbl
GROUP BY FLOOR(age/10)
Das ist in Ordnung, aber wir können den Bereich noch nicht sehen. Stattdessen sehen wir nur die berechneten Geschosswerte, die 0, 1, 2 ... n
sind.
Um die tatsächliche Untergrenze zu erhalten, müssen wir sie erneut mit 10 multiplizieren, um 0, 10, 20 ... n
zu erhalten:
FLOOR(age/10) * 10
Wir brauchen auch die obere Grenze jedes Bereichs, der unter bound + 10 - 1 oder ist
FLOOR(age/10) * 10 + 10 - 1
Schließlich verketten wir beide zu einer Zeichenfolge wie folgt:
TO_CHAR(FLOOR(age/10) * 10) || '-' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1)
Dies erzeugt '0-9', '10-19', '20-29'
usw.
Nun sieht unser SQL so aus:
SELECT
TO_CHAR(FLOOR(age/10) * 10) || ' - ' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1),
COUNT(*)
FROM tbl
GROUP BY FLOOR(age/10)
Wenden Sie abschließend eine Bestellung und Nice-Spaltenaliasnamen an:
SELECT
TO_CHAR(FLOOR(age/10) * 10) || ' - ' || TO_CHAR(FLOOR(age/10) * 10 + 10 - 1) AS range,
COUNT(*) AS frequency
FROM tbl
GROUP BY FLOOR(age/10)
ORDER BY FLOOR(age/10)
In komplexeren Szenarien werden diese Bereiche jedoch möglicherweise nicht in konstanten Chunks der Größe 10 gruppiert, sondern müssen dynamisch gruppiert werden. Oracle verfügt über erweiterte Histogrammfunktionen, die unter http://docs.Oracle.com/cd enthalten sind /E16655_01/server.121/e15858/tgsql_histo.htm#TGSQL366
Dank an @MatthewFlaschen für seinen Ansatz; Ich habe nur die Details erklärt.
Hier ist eine Lösung, die eine "Bereichstabelle" in einer Unterabfrage erstellt und diese dann verwendet, um die Daten aus der Haupttabelle zu partitionieren:
SELECT DISTINCT descr
, COUNT(*) OVER (PARTITION BY descr) n
FROM age_table INNER JOIN (
select '1-10' descr, 1 rng_start, 10 rng_stop from dual
union (
select '11-20', 11, 20 from dual
) union (
select '20+', 21, null from dual
)) ON age BETWEEN nvl(rng_start, age) AND nvl(rng_stop, age)
ORDER BY descr;
Ich musste die Daten danach gruppieren, wie viele Transaktionen in einer Stunde erschienen. Ich tat dies, indem ich die Stunde aus dem Zeitstempel extrahierte:
select extract(hour from transaction_time) as hour
,count(*)
from table
where transaction_date='01-jan-2000'
group by
extract(hour from transaction_time)
order by
extract(hour from transaction_time) asc
;
Ausgabe geben:
HOUR COUNT(*)
---- --------
1 9199
2 9167
3 9997
4 7218
Wie Sie sehen, ist dies eine sehr einfache Möglichkeit, die Anzahl der Datensätze pro Stunde zu gruppieren.
fügen Sie stattdessen eine age_range-Tabelle und ein age_range_id-Feld zu Ihrer Tabelle und Gruppe hinzu.
// entschuldige die DDL, aber du solltest die Idee bekommen
create table age_range(
age_range_id tinyint unsigned not null primary key,
name varchar(255) not null);
insert into age_range values
(1, '18-24'),(2, '25-34'),(3, '35-44'),(4, '45-54'),(5, '55-64');
// Entschuldige die DML erneut, aber du solltest die Idee bekommen
select
count(*) as counter, p.age_range_id, ar.name
from
person p
inner join age_range ar on p.age_range_id = ar.age_range_id
group by
p.age_range_id, ar.name order by counter desc;
Sie können diese Idee verfeinern, wenn Sie möchten - fügen Sie Spalten from_age to_age in der Tabelle age_range usw. hinzu - aber ich überlasse Ihnen dies.
hoffe das hilft :)
Ich musste am Tag eine Anzahl von Proben sammeln. Inspiriert von @Clarkey habe ich TO_CHAR verwendet, um das Datum des Samples aus dem Zeitstempel in ein ISO-8601-Datumsformat zu extrahieren, und dieses in den GROUP BY- und ORDER BY-Klauseln verwendet. (Weitere inspiriert, ich poste es auch hier, falls es für andere nützlich ist.)
SELECT
TO_CHAR(X.TS_TIMESTAMP, 'YYYY-MM-DD') AS TS_DAY,
COUNT(*)
FROM
TABLE X
GROUP BY
TO_CHAR(X.TS_TIMESTAMP, 'YYYY-MM-DD')
ORDER BY
TO_CHAR(X.TS_TIMESTAMP, 'YYYY-MM-DD') ASC
/
Wenn Sie Oracle 9i + verwenden, können Sie möglicherweise die Analysefunktion NTILE
verwenden :
WITH tiles AS (
SELECT t.age,
NTILE(3) OVER (ORDER BY t.age) AS tile
FROM TABLE t)
SELECT MIN(t.age) AS min_age,
MAX(t.age) AS max_age,
COUNT(t.tile) As n
FROM tiles t
GROUP BY t.tile
Die Einschränkung bei NTILE besteht darin, dass Sie nur die Anzahl der Partitionen angeben können , nicht die Haltepunkte selbst. Sie müssen also eine geeignete Nummer angeben. IE: Mit 100 Zeilen weist NTILE(4)
jeder der vier Buckets/Partitionen 25 Zeilen zu. Sie können Analysefunktionen nicht verschachteln, daher müssen Sie sie mit Unterabfragen/Unterabfrage-Factoring überlagern, um die gewünschte Granularität zu erzielen. Ansonsten verwende:
SELECT CASE t.age
WHEN BETWEEN 1 AND 10 THEN '1-10'
WHEN BETWEEN 11 AND 20 THEN '11-20'
ELSE '21+'
END AS age,
COUNT(*) AS n
FROM TABLE t
GROUP BY CASE t.age
WHEN BETWEEN 1 AND 10 THEN '1-10'
WHEN BETWEEN 11 AND 20 THEN '11-20'
ELSE '21+'
END
Mein Ansatz:
select range, count(1) from (
select case
when age < 5 then '0-4'
when age < 10 then '5-9'
when age < 15 then '10-14'
when age < 20 then '15-20'
when age < 30 then '21-30'
when age < 40 then '31-40'
when age < 50 then '41-50'
else '51+'
end
as range from
(select round(extract(day from feedback_update_time - feedback_time), 1) as age
from txn_history
) ) group by range
Können Sie die folgende Lösung ausprobieren:
SELECT count (1), '1-10' where age between 1 and 10
union all
SELECT count (1), '11-20' where age between 11 and 20
union all
select count (1), '21+' where age >20
from age