web-dev-qa-db-fra.com

Nombre d'articles MySQL dans la "clause"

J'ai trois tableaux pour définir les utilisateurs:

USER: user_id (int), username (varchar)
USER_METADATA_FIELD: user_metadata_field_id (int), field_name (varchar)
USER_METADATA: user_metadata_field_id (int), user_id (int), field_value (varchar)

Je voudrais créer un utilisateur de niveau intermédiaire qui a un certain accès aux autres utilisateurs de l'application. Pour déterminer les utilisateurs auxquels l'utilisateur connecté peut accéder, j'utilise une sous-requête comme celle-ci:

SELECT user_id FROM user WHERE user_id 
     IN (SELECT user_id 
         FROM user_metadata 
         WHERE user_metadata_field_id = 1 AND field_value = 'foo')

Actuellement, je stocke la chaîne de sous-requête dans une variable, puis je l'insère dynamiquement dans la requête externe chaque fois que j'ai besoin de tirer une liste d'utilisateurs. Après avoir fait cela, je me suis dit: "Il doit être préférable de simplement stocker une chaîne de la user_ids ".

Donc, au lieu de le stocker dans une variable ...

$subSql = "SELECT user_id FROM user_metadata WHERE user_metadata_field_id = 1 AND field_value = 'foo'";

... j'effectue la requête et stocke le résultat comme ceci ...

$subSql = "12, 56, 89, 100, 1234, 890";

Ensuite, lorsque j'ai besoin de tirer un lit d'utilisateurs auxquels l'utilisateur connecté a accès, je peux le faire avec:

$sql = "SELECT user_id FROM user WHERE user_id IN ($subSql)";

Et enfin les questions:

Combien d'éléments pouvez-vous utiliser dans une CLAUSE _ MySQL IN? Le stockage des identifiants réels au lieu de l'instruction sub-sql doit être plus rapide pour effectuer cette requête externe à chaque fois, non?

62
Bart

À partir d'un certain nombre, les tables IN sont plus rapides.

MySQL contient quelque chose dans son code qui rend la construction d'une plage sur un grand nombre de valeurs constantes plus lente que la même chose dans une boucle imbriquée.

Voir cet article dans mon blog pour plus de détails sur les performances:

34
Quassnoi

Depuis le manuel :

Le nombre de valeurs dans la liste IN n'est limité que par le max_allowed_packet valeur.

151
RedFilter

Comme indiqué dans la réponse de Quassnoi, un bute sur d'autres considérations pratiques, avant d'atteindre toute limite possible imposé par l'implémentation d'une version MySql donnée (*). Par conséquent, à mesure que le nombre d'utilisateurs administratifs (ou d'autres critères pouvant nécessiter une construction IN) augmente, il convient de chercher à utiliser des alternatives à un "IN" littéral, comme l'utilisation de tables temporaires (ou même permanentes).

Étant donné que vous envisagez une gestion spéciale des critères "utilisateur administrateur", à des fins de performances, je voudrais faire un commentaire et une suggestion.

Commentaire: Serait-ce un cas d'optimisation prématurée?
Je ne suis pas au courant des spécificités de cette base de données, de son volume, de sa complexité, etc. Et, oui, je connais certains des avantages de performance à payer au format EAV (Entity-Attribute-Value), mais Je pense que même pour les entreprises prospères, la base de données des comptes compte rarement plus de 10 000 utilisateurs. Donc, même avec de très nombreux attributs par utilisateur, nous examinons toujours une table EAV relativement petite, qui peut ne pas nécessiter ce type d'optimisation. (D'un autre côté, quelques autres astuces d'optimisation peuvent être les bienvenues dans d'autres domaines).
De plus, les cas d'utilisation typiques impliquent relativement peu de requêtes dans la base de données de comptes, par rapport à d'autres requêtes, et c'est donc une autre raison de différer toute considération de performance non triviale pour les fonctionnalités liées aux comptes de l'application.

Suggestion: Peut-être utiliser des "attributs re-normalisés"
Pour les attributs à valeur unique, et en particulier s'ils sont courts, ils peuvent être déplacés (ou dupliqués) dans la table Entity (table 'USER' dans ce cas). Cela introduit un peu de logique au moment où les éléments sont insérés ou mis à jour, mais cela équivaut à de nombreuses jointures (ou sous-requêtes) et offre également la possibilité d'envisager des index multi-champs pour prendre en charge les cas d'utilisation les plus courants.

(*) Y a-t-il un limt?
Je n'ai pas lu sur une telle limite; Je sais qu'Oracle a (avait) une limite de 1 000 à un moment donné, MSSQL n'en a pas; bien sûr, tous les serveurs ont une limite basée sur la longueur totale de l'instruction SQL, mais c'est un très grand nombre! si jamais on tombe dessus, il/elle a d'autres problèmes ... ;-)

11
mjv

La clause IN de MySQL elle-même n'a pas une telle limite. J'ai essayé avec 8000 éléments son travail bien pour moi. L'erreur de débordement de pile peut être de la variable déclarée,

7
Hidayat