it-swarm.com.de

Oracle: Wie gruppiere ich mich über einen Bereich?

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. 

29
Mark Harrison
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
55
Einstein

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);
25

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:

  • 0-9
  • 10-19
  • 20 - 29
  • usw.

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.

9
Wintermute

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;
3
Dan

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.

2
Clarkey

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 :)

1
Jon Black

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
/
1
Kieron Hardy

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
1
OMG Ponies

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  
  • Ich kann die Bereiche flexibel definieren
  • Ich wiederhole die Bereiche nicht in Auswahl- und Gruppenklauseln
  • aber jemand sagt mir bitte, wie ich sie nach Größen ordnen kann!
0
Ananth N

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 
0
sudarshan vp