2016-08-08 13 views
0

Ich habe eine Abfrage, die einige aggregierte Statistiken nach AltersgruppeOracle SQL SELECT-Anweisung, die Zahlen 1-100 für eine Nachschlagetabelle generiert?

Agegroup Freq 
    0-5   2.3 
    6-10  3.2 
    11-15  3.6 

Aus verschiedenen Gründen zieht, muß ich die Ausgangsdaten eine Lookup-Tabelle 1-100 im folgende Format

Age Agegroup Freq 
    1 0-5   2.3 
    2 0-5   2.3 
    3 0-5   2.3 
    4 0-5   2.3 
    5 0-5   2.3 
    6 6-10  3.2 
    7 6-10  3.2 
    8 6-10  3.2 
    9 6-10  3.2 
    10 6-10  3.2 
... 
für jedes Alter sein

Wie könnte ich das tun? Ich bin nicht in der Lage, Tabellen zu erstellen, also denke ich, wenn es eine Möglichkeit gibt, eine Art Select-Anweisung zu schreiben, die alle Altersgruppen von 1 bis 100 und die Altersgruppe enthält und dann zu der ursprünglichen Abfrage mit den berechneten Häufigkeiten hinzufügt Altersklasse - so etwas wie dies

SELECT t1.age, [case when statemenet that assigns the correct age group from t1.Age] "Agegroup" 

FROM ([statemement that generates numbers 1-100] "age") t1 

JOIN (Original query that creates the aggreated agegroup data) t2 on t1.Agegroup = t2.Agegroup 

So habe ich zwei Fragen

  1. Ist dies ein Ansatz, der überhaupt Sinn macht?
  2. Ist es möglich, den t1 zu generieren, den ich suche? I.e. eine select-Anweisung, die eine t1 der Form schaffen

    Age Agegroup 
    1 0-5 
    2 0-5 
    3 0-5 
    4 0-5 
    5 0-5 
    6 6-10 
    7 6-10 
    8 6-10 
    9 6-10 
    10 6-10 
    

    ...

, die dann mit der Abfrage verbunden werden könnten, die die Häufigkeit von Altersklasse hat?

+2

Dies wird Zahlen generieren 1-100: 'wählen rownum als n von Dual connect by level <= 100; ' –

+3

Mögliches Duplikat von [SQL zum Generieren einer Liste von Zahlen von 1 bis 100] (http://stackoverflow.com/questions/2847226/sql-to-generate-a-list-of- Zahlen von 1 bis 100) – sstan

+0

Ihr exa mple Ausgabe hat kein Alter 0, ist das absichtlich? Sind alle Bereiche 5 Jahre (außer der ersten, wenn Null enthalten ist)? Und ist es möglich, stattdessen die ursprüngliche Abfrage zu modifizieren - vielleicht könnte dies beispielsweise mit analytischen Funktionen auf einmal gemacht werden. –

Antwort

0

Ein alternativer Ansatz, wenn Sie 11gR2 oder höher verwenden, besteht darin, recursive subquery factoring mit einem regulären Ausdruck zu verwenden, um das untere und obere Alter in jedem Bereich aus Ihrem Zeichenfolgenwert zu extrahieren.

with original_query (agegroup, freq) as (
    -- Original query that creates the aggreated agegroup data 
    select '0-5', 2.3 from dual 
    union all select '6-10', 3.2 from dual 
    union all select '11-15', 3.6 from dual 
), 
r (age, agegroup, freq) as (
    select to_number(regexp_substr(agegroup, '\d+', 1, 1)), agegroup, freq 
    from original_query 
    union all 
    select age + 1, agegroup, freq 
    from r 
    where age < to_number(regexp_substr(agegroup, '\d+', 1, 2)) 
) 
select age, agegroup, freq 
from r 
order by age; 

     AGE AGEGR  FREQ 
---------- ----- ---------- 
     0 0-5   2.3 
     1 0-5   2.3 
     2 0-5   2.3 
     3 0-5   2.3 
     4 0-5   2.3 
     5 0-5   2.3 
     6 6-10   3.2 
     7 6-10   3.2 
     8 6-10   3.2 
     9 6-10   3.2 
     10 6-10   3.2 
     11 11-15  3.6 
     12 11-15  3.6 
     13 11-15  3.6 
     14 11-15  3.6 
     15 11-15  3.6 

Das Ankerelement bekommt jede ursprüngliche Zeile aus Ihrer bestehenden Ergebnismenge, und extrahiert die untere gebundene Nummer (0, 6, 11, ...) einen einfachen regulären Ausdruck - die auch mit getan werden könnte, substr/instr.

Das rekursive Element wiederholt dann jede dieser Ankerzeilen und fügt jedes Mal eine Zahl zum Alter hinzu, bis die Obergrenze des Bereichs erreicht ist.

Sie könnten auch connect by verwenden, aber es ist ein bisschen peinlicher mit mehreren Quellzeilen.

1

So etwas wie ... Ich habe Alter 0 (es kann ausgeschlossen werden, wenn nötig), und ich ging nur durch Alter 15. Das ist hart-codiert; mit ein wenig mehr Arbeit kann es gemacht werden, um das höchste Alter in den Bereichen zusammenzubringen.

Diese Version funktioniert nicht unnötig, weil sie die Teilstrings wiederholt berechnet. Es kann immer noch in weniger als einer Sekunde ausgeführt werden, aber wenn die Leistung wichtig wird, kann es geschrieben werden, um diese Teilstrings in einem CTE zuerst zu berechnen, so dass sie nicht wiederholt berechnet werden. (Hier nicht dargestellt.)

with 
    inputs (agegroup, freq) as (
     select '0-5', 2.3 from dual union all 
     select '6-10', 3.2 from dual union all 
     select '11-15', 3.6 from dual 
    ) 
select c.age, i.agegroup, i.freq 
from (select level - 1 as age from dual connect by level <= 16) c 
     inner join inputs i 
     on age between to_number(substr(i.agegroup, 1, instr(i.agegroup, '-') - 1)) 
       and  to_number(substr(i.agegroup, instr(i.agegroup, '-') + 1)) 
order by age 
; 

Ausgang:

AGE AGEGROUP  FREQ 
---- --------- ---------- 
    0 0-5    2.3 
    1 0-5    2.3 
    2 0-5    2.3 
    3 0-5    2.3 
    4 0-5    2.3 
    5 0-5    2.3 
    6 6-10    3.2 
    7 6-10    3.2 
    8 6-10    3.2 
    9 6-10    3.2 
    10 6-10    3.2 
    11 11-15   3.6 
    12 11-15   3.6 
    13 11-15   3.6 
    14 11-15   3.6 
    15 11-15   3.6 

16 rows selected. 
1

Hier ist eine andere Lösung ist, eine hierarchische Abfrage verwenden. Es benötigt keine "magischen Zahlen" mehr, die Alter werden logisch durch die Bereiche bestimmt, und es gibt keinen Join (außer was die Abfrage-Engine hinter den Kulissen in der hierarchischen Abfrage tut).Bei dem zugegebenermaßen sehr kleinen Beispiel, das Sie zur Verfügung gestellt haben, sind die Kosten für den Optimierer ungefähr 20% geringer als bei der von mir bereitgestellten Join-basierten Lösung - das kann zu einer etwas schnelleren Ausführung führen.

(HINWEIS - I posted zwei verschiedene Lösungen, damit ich diese separate Antworten glauben -.. Im Gegensatz meine früheren Post zu bearbeite ich nicht sicher war, welche Aktion geeignet ist)

Auch eine andere beachten Sie, dass bestätigen @ Alex Poole erwähnte diesen Ansatz in seinem Beitrag; Ich habe es bisher nicht gesehen, oder ich hätte es von Anfang an anerkannt.

with 
    inputs (agegroup, freq) as (
     select '0-5', 2.3 from dual union all 
     select '6-10', 3.2 from dual union all 
     select '11-15', 3.6 from dual 
    ) 
select to_number(substr(agegroup, 1, instr(agegroup, '-') - 1)) + level - 1 as age, 
     agegroup, freq 
from inputs 
connect by level <= 1 + to_number(substr(agegroup, instr(agegroup, '-') + 1)) - 
         to_number(substr(agegroup, 1, instr(agegroup, '-') - 1)) 
     and prior agegroup = agegroup 
     and prior sys_guid() is not null 
order by age 
; 
+0

Das ist, was ich gemeint habe, aber sehr vage * 8-) Ich bevorzuge rekursive CTEs gegenüber dem Zwingen der nicht-detrmistischen Prioritätsklausel jetzt, aber das könnte besser sein auf solch einem kleinen Datensatz. –

+1

@AlexPoole - Ich bevorzuge auch rekursive CTEs im Allgemeinen; einige (nicht ganz formale) Tests, die ich mit der Aufteilung großer CSVs gemacht habe, scheinen darauf hinzudeuten, dass die rekursive Abfrage wesentlich schneller ist als hierarchische Abfragen. Ein Vorteil von hierarchischen Abfragen besteht darin, dass sie mit Versionen vor 11.2 verwendet werden können. – mathguy

+0

Ja, soll das auch erwähnen. –

0

Antworten auf Fragen:

  1. Ja, das ist eine gute Idee, mit Join Ansatz mit generierten Tabelle "t1".

    SELECT age as "Age", 
         CASE l_age WHEN 0 THEN 0 ELSE l_age + 1 END || '-' || r_age AS "Agegroup" 
        FROM (
        SELECT lvl age, 
         CASE m5 WHEN 0 THEN (t5-1)*5 ELSE t5 *5 END l_age, 
         CASE m5 WHEN 0 THEN t5 *5 ELSE (t5+1)*5 END r_age 
        FROM (
        SELECT /*+ cardinality(100) */ 
          level lvl, mod(level, 5) m5, TRUNC(level/5) t5 
        FROM dual 
        CONNECT BY level <= 100 
    ) 
    ); 
    

Ausgabe::

  • Tabelle "t1" erzeugen können Sie nächste Abfrage verwenden

      Age Agegroup 
          1 0-5 
          2 0-5 
          3 0-5 
          4 0-5 
          5 0-5 
          6 6-10 
          7 6-10 
          8 6-10 
          9 6-10 
          10 6-10 
          11 11-15 
          12 11-15 
          13 11-15 
          14 11-15 
          15 11-15 
          16 16-20 
          17 16-20 
          18 16-20 
          19 16-20 
          20 16-20 
          21 21-25 
          22 21-25 
          23 21-25 
          24 21-25 
          25 21-25 
          26 26-30 
          27 26-30 
          28 26-30 
          29 26-30 
          30 26-30 
          ......... 
    
          80 76-80 
          81 81-85 
          82 81-85 
          83 81-85 
          84 81-85 
          85 81-85 
          86 86-90 
          87 86-90 
          88 86-90 
          89 86-90 
          90 86-90 
          91 91-95 
          92 91-95 
          93 91-95 
          94 91-95 
          95 91-95 
          96 96-100 
          97 96-100 
          98 96-100 
          99 96-100 
          100 96-100 
    
  • Verwandte Themen