2011-12-27 3 views
1

Le schéma:

J'ai 3 tableaux:Requête complexe avec des tables connexes - Solution optimale

  • utilisateur
  • Feature
  • User_has_Feature:

initialement tous les utilisateurs n'a pas caractéristiques

données Exemple:

utilisateur:

| id | name | 
| 1 | Rex | 
| 2 | Job | 

Feature:

| id | name | 
| 1 | Eat | 
| 2 | Walk | 

User_has_Feature:

| id | user_id | feature_id | have_feature | 
| 1 | 1  | 1   | true   | 
| 2 | 1  | 1   | true   | 
| 3 | 2  | 2   | true   | 
| 4 | 2  | 2   | false  | 

Les questions sont les suivantes:

  • ¿Comment obtenir uniquement les enregistrements qui ont toutes les fonctionnalités? (explicitement)

Exemple:

| user_name | feature_name | feature_status | 
| Rex  | Eat   | true   | 
| Rex  | Walk   | true   | 
  • Comment obtenir des enregistrements qui ne sont pas toutes les fonctionnalités? (Encore une fois explicitement)

Exemple:

| user_name | feature_name | feature_status | 
| Job  | Eat   | true   | 
| Job  | Walk   | false   | 

Certaines conditions doivent être assisté

  • J'ai besoin la liste des utilisateurs avec toutes les fonctionnalités (vrai ou faux) dans les deux requêtes comme des exemples
  • L'utilisateur ont 650k enregistrements (pour l'instant)
  • Fonctionnalités ont 45 enregistrements (pour l'instant)
  • Est une requête de temps.

L'idée est d'exporter le résultat vers un fichier CSV


précoce Solution

grâce aux réponses (@RolandoMySQLDBA, Ingram @ Tom, @DRapp) J'ai trouvé une solution :

SELECT u.name, f.name, IF(uhf.status=1,'YES','NO') as status 
FROM user u 
    JOIN user_has_feature uhf ON u.id = uhf.user_id 
    JOIN feature f ON f.id = uhf.feature_id 
    JOIN 
     (
     SELECT u.id as id 
     FROM user u 
      JOIN user_has_feature uhf ON uhf.user_id = u.id 
     WHERE uhf.status = 1 
     GROUP BY u.id 
     HAVING count(u.id) <= (SELECT COUNT(1) FROM feature) 
     ) as `condition` ON `condition`.id = u.id 
ORDER BY u.name, f.id, uhf.status 

pour obtenir des enregistrements qui ne sont pas toutes les fonctionnalités et obtenir tous les enregistrements qui ont toutes les caractéristiques changent:

  • WHERE uhf.status = 1 par WHERE uhf.status = 2
  • HAVING count(u.id) <= (SELECT COUNT(1) FROM feature) par HAVING count(u.id) = (SELECT COUNT(1) FROM feature)

mais je veux savoir si cela est une solution?

Répondre

1
SELECT 
    UNF.*, 
    IF(
     (LENGTH(UNF.FeatureList) - LENGTH(REPLACE(UNF.FeatureList,',',''))) 
     = (FC.FeatureCount - 1),'Has All Features','Does Not Have All Features' 
    ) HasAllFeatures 
FROM 
    (SELECT 
     U.name user_name 
     GROUP_CONCAT(F.name) Features 
    FROM 
     (SELECT user_id,feature_id FROM User_has_Feature 
     WHERE feature_status = true) UHF 
     INNER JOIN User U ON UHF.user_id = U.id 
     INNER JOIN Feature F ON UHF.feature_id = F.id 
    GROUP BY 
     U.name 
    ) UNF, 
    (SELECT COUNT(1) FeatureCount FROM Feature) FC 
; 

Les sous-requête retourne UNF avec tous les utilisateurs répertoriés dans User_has_Feature et une liste séparée par des virgules des caractéristiques. La colonne HasAllFeatures est déterminée par le nombre de colonnes dans UNF.FeatureList. Dans votre cas, il y a deux fonctionnalités. Si le nombre de virgules dans UNF.FeatureList est FeatureCount - 1, l'utilisateur dispose de toutes les fonctionnalités. Sinon, l'utilisateur n'a pas toutes les fonctionnalités.

Voici une meilleure version qui montre tous les utilisateurs et si oui ou non ils ont tous, une partie ou aucune caractéristique

SELECT 
    U.name user_name, 
    IFNULL(UsersAndFeatures.HasAllFeatures, 
    'Does Not Have Any Features') 
    WhatFeaturesDoesThisUserHave 
FROM 
    User U LEFT JOIN 
    (
     SELECT 
      UHF.user_id id, 
      IF(
       (LENGTH(UHF.FeatureList) - LENGTH(REPLACE(UHF.FeatureList,',',''))) 
       = (FC.FeatureCount - 1), 
       'Has All Features', 
       'Does Not Have All Features' 
      ) HasAllFeatures 
     FROM 
      (
       SELECT user_id,GROUP_CONCAT(Feature.name) FeatureList 
       FROM User_has_Feature INNER JOIN Feature 
       ON User_has_Feature.feature_id = Feature.id 
       GROUP BY user_id 
      ) UHF, 
      (SELECT COUNT(1) FeatureCount FROM Feature) FC 
    ) UsersAndFeatures 
USING (id); 
+0

Le 'UNF.FeatureList' d'où vient – rkmax

+0

Désolé pour UNF. Le H et N sont l'un à côté de l'autre sur le clavier. – RolandoMySQLDBA

0

Voici mon bash à ce

  • créer une vue du général informations

    CREATE VIEW v_users_have_features AS 
    SELECT usr.id, usr.name, feature.name, has_feature.status 
        FROM usr 
        JOIN has_feature ON usr.id = has_feature.user_id 
        JOIN feature ON has_feature.feature_id = feature.id; 
    
  • utiliser la vue pour d'autres requêtes

    SELECT v_users_have_features.id, v_users_have_features.u_name, v_users_have_features.f_name 
    FROM v_users_have_features 
    GROUP BY v_users_have_features.id 
    HAVING COUNT(v_users_have_features.id) = (SELECT COUNT(feature.id) 
                   FROM feature 
                   WHERE feature.name = v_users_have_features.f_name) 
    

P.S. vous devrez peut-être adapter (en particulier le dernier) à vos besoins exacts vous pourriez également omettre de créer la vue et l'imbriquer dans la clause FROM comme dans une autre réponse il semblait plus facile de créer la vue

+0

besoin non seulement de l'utilisateur "Rex", mais tous les utilisateurs qui se conforment à la condition – rkmax

+0

Hehe juste l'omettre :-) –

+0

'Feature' n'ont pas de champ' user_id' – rkmax

-1

Comptez le nombre d'entités. Ecrire une requête sur les utilisateurs qui utilise une sous-requête corrélée pour trouver toutes les fonctionnalités d'un utilisateur et les compter. Faites en sorte que le critère de restriction dans la requête du haut soit l'égalité de ce nombre et le nombre global de caractéristiques.

Est-ce que MySQL peut faire des sous-requêtes corrélées? Sinon, vous devrez peut-être utiliser une meilleure base de données.

1
select 
     u.id, 
     u.name as User_Name, 
     f.name as Feature_Name, 
     uhf.feature_Status 
    from 
     (select uhf.user_id, 
       sum(if(uhf.feature_status, 1, 0)) as UserFeatureCount 
      from user_has_feature uhf 
      group by uhf.user_id) AllUsersWithCounts 

     join 
     (select count(*) as AllFeaturesCount 
      from Feature) AllFeatures 
     on AllUsersWithCounts.UserFeatureCount = AllFeatures.AllFeaturesCount 

     join user u 
     on AllUsersWithCounts.user_id = u.ID 

     join user_has_feature uhf 
     on AllUsersWithCounts.User_id = uhf.user_id 
     join feature f 
      on uhf.feature_id = f.id 

La requête ci-dessus devrait permettre à toutes les personnes qui ont explicitement toutes les caractéristiques. Afin d'obtenir ceux qui n'ont pas toutes les fonctionnalités, il suffit de changer la jointure de = à <

on AllUsersWithCounts.UserFeatureCount < AllFeatures.AllFeaturesCount 
Questions connexes