2011-08-04 2 views
1

J'ai une table géante qui a des milliards de dossiers comme celui-ci:Oracle comment partitionner les données et obtenir des enregistrements à chaque 10%

ID | H | N | Q | other 
-----+-----+------+-----+-------- 
AAAA | 0 | 7 | Y | ... 
BBBB | 1 | 5 | Y | ... 
CCCC | 0 | 11 | N | ... 
DDDD | 3 | 123 | N | ... 
EEEE | 6 | 4 | Y | ... 

Ces quatre colonnes font partie d'un indice. Ce que je veux faire est de construire une requête qui me donne la 1ère ligne, suivie de la ligne à 10%, 20%, 30%, 40%, ... de sorte que la requête me donnera toujours 10 lignes quelle que soit la taille la table est (aussi longue que # rows> = 10).

Est-ce encore possible avec SQL? Si oui, comment le ferais-je? Quel genre de caractéristiques de performance at-il?

+1

Qu'est-ce que vous commandez par pour déterminer ce qui est au 10% de position dans le tableau? Juste par 'ID'? –

+0

Oui, ID et N. H est une valeur pré-calculée basée sur ID uniquement. –

Répondre

3

Une option serait

SELECT id, 
     h, 
     n, 
     q 
    FROM (
    SELECT id, 
      h, 
      n, 
      q, 
      row_number() over (partition by decile order by id, n) rn 
     FROM (
     SELECT id, 
       h, 
       n, 
       q, 
       ntile(10) over (order by id, n) decile 
      FROM your_table 
      ) 
     ) 
    WHERE rn = 1 

Il y a probablement une approche plus efficace en utilisant PERCENTILE_DISC ou CUME_DIST qui ne clique pas pour moi en ce moment. Mais cela devrait fonctionner.

+0

Je vais devoir l'accepter mais c'est trop lent pour mes besoins. J'ai fini par déterminer combien de lignes la table a à l'avance et ensuite faire des requêtes de 1/1000e qui font un 'max (id) où id>? et rownum <=? 'alors je prends juste quel que soit le dernier id et le rebranche à la requête et stocke tous les 100, jusqu'à ce que je reçois tous les 10. Il semble être beaucoup plus rapide que cette requête de partition .. Je vais tester demain et revenons à vous. –

0

Vous pouvez utiliser un histogramme pour obtenir cette information. L'inconvénient majeur est que les résultats ne seront qu'approximatifs, et il est très difficile de dire à quel point ils seront approximatifs. Et vous devrez rassembler des statistiques de table pour actualiser les résultats, mais vous le faites probablement déjà. Du côté positif, la requête pour obtenir les résultats sera très rapide. Et en utilisant des statistiques au lieu d'une requête serait donc cool.

Voici une démonstration rapide:

--Create a table with the IDs AA - ZZ. 
create table test(id varchar2(100), h number, n number, q varchar2(100) 
    ,other varchar2(100)); 

insert into test 
select letter1||letter2 letters, row_number() over (order by letter1||letter2), 1, 1, 1 
from 
    (select chr(65+level-1) letter1 from dual connect by level <= 26) letters1 
    cross join 
    (select chr(65+level-1) letter2 from dual connect by level <= 26) letters2 
; 
commit; 

--Gather stats, create a histogram with 11 buckets (we'll only use the first 10) 
begin 
    dbms_stats.gather_table_stats(user, 'TEST', cascade=>true, 
     method_opt=>'FOR ALL COLUMNS SIZE AUTO, FOR COLUMNS SIZE 10 ID'); 
end; 
/

--Getting the values from user_histograms is kinda tricky, especially for varchars. 
--There are problems with rounding, so some of the values may not actually exist. 
-- 
--This query is from Jonathan Lewis: 
-- http://jonathanlewis.wordpress.com/2010/10/05/frequency-histogram-4/ 
select 
     endpoint_number, 
     endpoint_number - nvl(prev_endpoint,0) frequency, 
     hex_val, 
     chr(to_number(substr(hex_val, 2,2),'XX')) || 
     chr(to_number(substr(hex_val, 4,2),'XX')) || 
     chr(to_number(substr(hex_val, 6,2),'XX')) || 
     chr(to_number(substr(hex_val, 8,2),'XX')) || 
     chr(to_number(substr(hex_val,10,2),'XX')) || 
     chr(to_number(substr(hex_val,12,2),'XX')), 
     endpoint_actual_value 
from (
     select 
       endpoint_number, 
       lag(endpoint_number,1) over(
         order by endpoint_number 
       )              prev_endpoint, 
       to_char(endpoint_value,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')hex_val, 
       endpoint_actual_value 
     from 
       user_histograms 
     where table_name = 'TEST' 
     and  column_name = 'ID' 
     ) 
where 
     endpoint_number < 10 
order by 
     endpoint_number 
; 

Voici une comparaison des résultats de l'histogramme avec les résultats réels de la requête de @Justin Cave:

Histogram: Real results: 
[email protected]   AA 
CP   CQ 
FF   FG 
HV   HW 
KL   KM 
NB   NC 
PR   PS 
SG   SH 
UU   UW 
XK   XL 
Questions connexes