具有相关表的复杂查询 - 最优解

时间:2011-12-27 18:23:30

标签: mysql

架构:

我有3张桌子:

  • 用户
  • 功能
  • User_has_Feature:

最初所有用户都没有功能

示例数据:

用户:

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

特点:

| 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        |

问题是:

  • ¿如何只获取具有所有功能的记录? (的明确地

示例:

| user_name | feature_name | feature_status |
| Rex       | Eat          | true           |
| Rex       | Walk         | true           |
  • 如何获取没有所有功能的记录? (再次明确

示例:

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

必须参加一些条件

  • 我需要两个查询中包含所有功能(真或假)的用户列表类似示例
  • 用户有650k记录(现在)
  • 功能有45条记录(现在)
  • 是一次查询。

我们的想法是将结果导出为CSV文件


早期解决方案

感谢(@RolandoMySQLDBA,@ Tom Ingram,@ DRapp)的答案,我找到了一个解决方案:

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

对于获取没有所有功能的记录,并获取所有功能都发生变化的记录:

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

但我想知道这是否是最佳解决方案

4 个答案:

答案 0 :(得分: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
;

UNF子查询返回User_has_Feature中列出的所有用户以及以逗号分隔的功能列表。列HasAllFeaturesUNF.FeatureList中的列数决定。在您的情况下,有两个功能。如果UNF.FeatureList中的逗号数是FeatureCount - 1,则该用户具有所有功能。否则,用户没有所有功能。

这是一个更好的版本,显示所有用户以及他们是否具有全部,部分或无功能

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);

答案 1 :(得分: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

以上查询应该让所有明确拥有所有功能的人都可以。为了获得那些没有所有功能的人,只需将一个连接从=更改为&lt;

 on AllUsersWithCounts.UserFeatureCount < AllFeatures.AllFeaturesCount

答案 2 :(得分:0)

这是我的bash

  • 创建一般信息的视图

    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;
    
  • 将视图用于其他查询

    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。您可能需要适应(特别是后者)您的确切要求,您也可以省略创建视图并将其嵌套在FROM子句中,就像在另一个答案中一样,创建视图似乎更方便

答案 3 :(得分:-1)

计算要素数量。在使用相关子查询的用户上编写查询,以查找用户拥有的所有功能并对其进行计数。在顶部查询中设置限制条件,使该计数与全局特征数相等。

MySQL能否进行相关子查询?如果没有,您可能需要使用更好的数据库。