2017-03-20 2 views
1

J'ai besoin d'exécuter une requête, qui sélectionne deux colonnes d'une grande table (3m + lignes, en sélectionnant deux colonnes, le jeu de résultats est d'environ 6-7m) et retourne une liste. J'ai donc utilisé union pour fusionner les colonnes dans une liste et aussi pour éliminer les doublons. Le problème est que je ne peux pas renvoyer le résultat dans une requête, j'ai besoin de le partitionner, donc j'ai appliqué un LIMIT ?,? aux sous-requêtes, que la couche d'application définit via Prepared Statements. Le problème: L'union élimine les doublons, mais seulement après l'application de LIMIT. Signification Si la requête deux renvoie 100 + 100 = 200 lignes et la plupart d'entre eux est un doublon, je retourne seulement < 200 lignes. Comment puis-je appliquer une limite à une telle requête, que je peux retourner un nombre spécifique de lignes? (Si j'appliquer la limite après les sous-requêtes, il faudra plus de deux minutes pour courir, il ne résoudra pas le problème.)Éliminer les doublons avant l'union

+0

'SELECT DISTINCT ...' – Psi

+0

@Psi L'utilisation de 'DISTINCT' permet de réaliser quoi? La même chose que d'utiliser 'Union' au lieu de' union tout ', le problème persiste. – appl3r

+0

Pourquoi avez-vous un «GROUP BY»? –

Répondre

2

Vous n'avez pas réellement besoin d'une sous-requête pour cela. Ce qui suit fonctionnera pour les 100 premières lignes:

(SELECT DISTINCT fs.smr as val 
    FROM `fr_search` as fs 
    ORDER BY val 
    LIMIT 100 
) 
UNION 
(SELECT DISTINCT fs.dmr as val 
    FROM `fr_search` as fs 
    ORDER BY val 
    LIMIT 100 
) 
ORDER BY val 
LIMIT 100; 

Cependant, une fois que vous commencez à mettre en offset, cela devient plus compliqué. Pour les 100 prochaines lignes:

(SELECT DISTINCT fs.smr as val 
    FROM `fr_search` as fs 
    ORDER BY val 
    LIMIT 200 
) 
UNION 
(SELECT DISTINCT fs.dmr as val 
    FROM `fr_search` as fs 
    ORDER BY val 
    LIMIT 200 
) 
ORDER BY val 
LIMIT 100, 100; 

Le problème est que vous ne savez pas d'où viendra le second ensemble.

Si vous avez réellement besoin de parcourir le jeu de résultats, je vous suggère de le stocker dans une table temporaire et une page hors de la table temporaire.

+0

J'ai utilisé une table temporaire pour résoudre ce problème. Fournit peu de frais généraux et des requêtes rapides et stables lors de l'exécution. – appl3r

0

Vous avez deux options:

Vous pouvez SELECT DISTINCT dans l'intérieur et extérieur requêtes:

SELECT DISTINCT val 
FROM 
(
    (SELECT DISTINCT fs.smr as val 
    FROM `fr_search` as fs) 

    UNION ALL 

    (SELECT DISTINCT fs.dmr as val 
    FROM `fr_search` as fs) 
) as vals 
ORDER BY val LIMIT ?,?; 

ou vous pouvez également regrouper par vos requêtes internes, avant le regroupement par la requête externe.

SELECT val 
FROM 
(
    (SELECT fs.smr as val 
    FROM `fr_search` as fs 
    GROUP BY fs.smr) 

    UNION ALL 

    (SELECT fs.dmr as val 
    FROM `fr_search` as fs 
    GROUP BY fs.dmr) 
) as vals 
GROUP BY val 
ORDER BY val LIMIT ?,?; 

Les deux feront essentiellement la même chose dans ce scénario particulier. Cependant, dans les deux cas, vous devez utiliser l'option "union all", de sorte que la partie UNION ne fonctionne pas seule et que vous définissez explicitement la manière dont vous souhaitez regrouper les enregistrements. Je voudrais également déplacer la clause limit à la requête externe

+0

L'utilisation de 'DISTINCT' ou' GROUP BY' sur les sous-unités semble fonctionner, mais cela prend le même temps que l'exécution des sous-requêtes sans limite et en l'appliquant après. – appl3r

+0

Votre requête va faire une analyse de table complète, peu importe ce que parce que vous voulez des valeurs distinctes. Je doute qu'il existe un moyen de l'optimiser. Était le but de la question comment obtenir les bons résultats ou était-ce comment rendre la requête plus rapide? –

+0

Les deux, je ne peux pas utiliser une requête qui prend des minutes à chaque appel. J'ai besoin de regarder dans des tables temporaires pour cela. – appl3r

1

L'optimisation des requêtes comporte toujours deux parties de la solution. Et est parfois un processus itératif d'essayer, mesurer et comparer. Écrivez une bonne (et plus précise) requête que le moteur peut fonctionner efficacement.

  1. Assurez-vous que les index appropriés sont disponibles afin que l'optimiseur puisse choisir un bon plan d'exécution.

La meilleure requête est très probablement la dernière ligne droite vers l'avant et simple:

SELECT v.val 
FROM (
     SELECT fs.smr as val 
     FROM `fr_search` as fs 
     UNION 
     SELECT fs.dmr as val 
     FROM `fr_search` as fs 
     ) as v 
ORDER BY v.val LIMIT ?,?; 

Pour fonctionner efficacement, vous aurez envie 2 index:

  • un sur fr_search.smr
  • l'autre sur fr_search.dmr

Si l'optimiseur ne peut pas gérer ce qui précède, essayez d'utiliser les indices d'indice pour forcer l'utilisation des index.

Dans un pincement extrême, vous pouvez essayer de forcer la question qui suit:

SELECT v.val 
FROM (
     SELECT DISTINCT fs.smr as val 
     FROM `fr_search` as fs 
     ORDER BY fs.smr LIMIT ? 
     UNION 
     SELECT DISTINCT fs.dmr as val 
     FROM `fr_search` as fs 
     ORDER BY fs.dmr LIMIT ? 
     ) as v 
ORDER BY v.val LIMIT ?,?; 

Notez que vos substitutions (en supposant que les pages de 100) devraient être les suivants:

 
Page 1: 100, 100, 100, 0 
Page 2: 200, 200, 100, 100 
Page 3: 300, 300, 100, 200 
Page 4: 400, 400, 100, 300 
etc. 

La raison en est , vous devez répondre à un éventuel déséquilibre de l'ordre des colonnes croisées en privilégiant l'une ou l'autre des tables. Ainsi par exemple page 4:

  • Obtenez les 400 premières lignes distinctes ordonnées par la clé de chaque colonne.
  • Renvoie les lignes 301 à 400 des données fusionnées.
  • Cela pourrait être les 400 dernières lignes de l'une des sous-requêtes. Mais il est plus probable de renvoyer environ 50 lignes de chaque sous-requête quelque part au-dessus de la marque de 150 lignes.
+0

Les deux index sont déjà définis, mais la taille de la table est de 1,2 Go. – appl3r

+0

L'optimiseur _peut être capable de gérer ceci avec les index appropriés sauf si vous avez un grand nombre de doublons. Cependant, il pourrait avoir du mal après avoir traversé plusieurs pages. –

+0

@ appl3r La taille de la table est indifférente si les index sont utilisés. Je n'ai pas Oracle, mais sur Sql Server: La requête ci-dessus sur une table avec 5 millions de lignes renvoie les 100 premières lignes avec 0 ms de temps CPU et 1 ms de temps écoulé. Le plan d'exécution montre une sélection d'un peu plus de 50 lignes pour chaque index qui sont ensuite fusionnées pour la sortie finale des 100 premiers. –