2017-09-11 3 views
0

J'ai une table avec 3 colonnes et des millions de lignes. tous sont des entiers (hachages) id, attribute, attrib_val Requête MySQL pour aller chercher n'importe quelle combinaison de n'existe pas/n'existe pas

id peut avoir plusieurs lignes avec des combinaisons de noms et de valeurs d'attribut.

La table a deux touches id, attribute, attrib_val attribute, attrib_val, id

J'ai besoin de construire dynamiquement, les requêtes qui peuvent atteindre ids basées sur des règles, par exemple:

ids dans lequel toutes sections ci-dessous doivent correspondre: attribute <x> contains value <y> or <t> attribute <l> does not contain value <f> or <c> ...

ids dans lesquels un des sections suivantes devrait correspondre: attribute <x> contains value <y> or <t> attribute <l> does not contain value <f> or <c> ...

Le problème: Ceci est la requête que je suis venu avec (je peux changer d'identifiant pas dans la ne contient pas de pièces et de modifier les ET à OU changer de ou à tout:

SELECT distinct id FROM attributes 
WHERE id IN (
    SELECT id FROM attributes 
    WHERE ((attribute = 12944489 AND attrib_value = 907348202) 
) 
AND id IN (
    SELECT id FROM attributes 
    WHERE ( 
    (attribute = 577513892 AND attrib_val = 519655334) 
    OR (attribute = 577513892 AND attrib_val = 1266247963) 
) 
) 
) 

Le problème est que cette requête n'est pas efficace. Pour une raison quelconque, Mysql balaye toutes les lignes de la table si je lance chaque sous-requête séparément, il contient quelques centaines de lignes.

Comment puis-je optimiser cette requête ou en trouver une alternative qui peut gérer les exigences flexibles efficacement. Notes: 1. Mysql 5.5.31 2. J'ai simplifié les requêtes pour une explication facile. en réalité, il existe une colonne sid globale supplémentaire et toutes les requêtes incluent sid = XXX dans chaque segment where.

+0

avec des jointures est beaucoup plus efficace que d'utiliser des instructions select imbriquées. Je recommande de commencer là et de voir quelles améliorations vous obtenez. – Archer

+0

N'utilisez pas 'IN (SELECT ...)', utilisez 'JOIN '. Un "auto-joint" dans ce cas. –

Répondre

1

Je suggère d'utiliser group by et having:

SELECT id 
FROM attributes 
WHERE (attribute, attrib_value) IN ((12944489, 907348202), (577513892, 519655334), (577513892, 1266247963)) 
GROUP BY id 
HAVING SUM((attribute, attrib_value) IN ((12944489, 907348202))) > 0 AND 
     SUM((attribute, attrib_value) IN ((577513892, 519655334), (577513892, 1266247963))) = 0; 
+0

thats une grande solution Gordon, merci, Y at-il un moyen de faire l'attribut ne contient pas la valeur ou (où il n'y a pas de partie «contient»? Je ne pense pas que cela fonctionnera parce que la partie WHERE et HAVING partie nier – Nir

+0

Il s'agit d'une solution inefficace car 'WHERE (a, b) IN ((1,2), ...) n'optimise pas _at all. (Cela ira si la table est suffisamment petite pour que le scan de la table soit OK .) –

+0

@RickJames ... Selon les notes de la documentation, cela a été corrigé en 5.7.3 (https://bugs.mysql.com/bug.php?id=31188) –

0
SELECT id 
    FROM a AS a1 
    WHERE attr = 11 AND val IN (22, 33) 
     AND NOT EXISTS (
       SELECT 1 FROM a 
        WHERE id = a1.id 
        AND attr = 44 
        AND val IN (55, 66)) 

PRIMARY KEY(id) -- Is this already there? If so, good for inner query 
INDEX(attr, val, id) -- needed for outer query