2017-10-06 15 views
2

Pour sélectionner les enregistrements N par catégorie, on peut faire:Comment interroger EFFICACEMENT n enregistrements par catégorie

SELECT category, category_id, value FROM 
(
    SELECT category, value, row_number() OVER (PARTITION by category) as category_id 
    FROM myTable 
) 
WHERE category_id < N; 

La SELECT INNER première partition les enregistrements par catégorie et à chaque dossier par catégorie un identifiant appelé category_id. La requête externe utilisera ensuite l'attribut category_id pour limiter le nombre d'enregistrements qu'il interroge par catégorie.

Ceci est extrêmement inefficace sur les tables BIG car il va en attribuant des identifiants à tous les enregistrements même si nous ne sommes intéressés que par N enregistrements par catégorie. Ce qui suit ne fonctionne pas sur le moteur SQL avec lequel je travaille - je ne sais pas si cela fonctionne sur un moteur du tout.

SELECT category, value, row_number() OVER (PARTITION by category) as category_id 
FROM myTable 
WHERE category_id < N 

Est-ce que quelqu'un connaît d'autres façons d'y parvenir avec une meilleure complexité dans le temps?

Plus pensées:

temps de profilage l'algorithme suivant contre requête ci-dessus pourrait fournir plus d'idées sur la façon dont la requête passe derrière la scène:

1. SELECT DISTINCT(category) FROM myTable 
    2. FOREACH category SELECT N rows 

plus d'informations: mes données être physiquement partitionné par category, être en mesure d'exploiter explicitement ce qui serait utile

+2

même si votre deuxième requête travaillerait sur certains SGBDR, le plan d'exécution serait probablement le même que le premier – Lamak

+0

vous pouvez essayer de vider votre table dérivée dans un #temp et créer un index sur cela, puis l'interroger – LONG

+1

Marquer votre question avec la base de données que vous utilisez. –

Répondre

4

Comme @Lamak mentio Dans un commentaire, vous ne pouvez pas éviter de trier toutes les lignes de la table, mais pas pour la raison indiquée. Un tri est nécessaire pour déterminer les catégories distinctes selon lesquelles le jeu de résultats doit être partitionné et, en l'absence d'ordonnancement explicite au sein de chaque partition, les numéros de ligne sont facilement déterminés comme un effet secondaire du tri par catégorie. Comment la requête s'exécute "dans les coulisses", ou, si elle utilise le bon terme, son plan d'exécution est déterminé par la présence (ou l'absence) d'un index qui pourrait aider à éviter ce tri de catégorie. Si vous aviez un index de couverture sur (category, value) et toutes les autres colonnes dont vous avez besoin dans le résultat, votre requête s'exécuterait beaucoup plus efficacement.

Dans ce dernier cas, l'algorithme simplifié pourrait ressembler à ceci:

  1. Lire les enregistrements pré-triés contenant toutes les colonnes nécessaires, y compris les numéros de ligne, de l'indice. Ignorer les enregistrements dont le numéro de ligne est supérieur à n.

Votre "idéal" requête

SELECT category, value, row_number() OVER (PARTITION by category) as 
category_id FROM myTable WHERE category_id < N 

ne serait probablement pas fonctionner dans une base de données SQL, car la liste SELECT est traitée après les WHERE prédicats clause, donc category_id est inconnue quand les prédicats sont évalués.

+0

Dans mon cas, la table est partitionnée physiquement sur hdfs par 'category', db2 ne semble pas assez intelligent pour tirer parti de cela. – r2d2oid

+1

Vous pouvez décrire la pile de votre logiciel plus en détail. DB2 ne sait pas ou ne se soucie pas de hdfs. Parlez-vous de BigSQL, peut-être? – mustaccio

+0

oui, le backend est ibm 'bigsql' – r2d2oid

0

Autre méthode de rownumber, mais j'ai aussi des doutes sur les performances. Je suis d'accord @mustaccio.Mon exemple prendre 5 lignes ...

select distinct f1.category, f3.*    
from yourtable f1       
inner join lateral           
(               
select f2.value from yourtable f2    
where f2.category=f1.category 
fetch first 5 rows only          
) f3 on 1=1