【问题标题】:MySQL query which has multiple subqueries, each with different join types具有多个子查询的 MySQL 查询,每个子查询具有不同的连接类型
【发布时间】:2012-05-12 07:35:16
【问题描述】:

首先让我给你一张我的 mysql 查询中使用的表之间的关系图:


(来源:r717.net

我的查询如下所示:

SELECT  * 
FROM    `permissions`
WHERE   `id` IN (
    SELECT      pr.perm_id 
    FROM        `user_roles` as ur 
    LEFT JOIN   `permissions_role` as pr 
    ON          ur.role_id = pr.role_id
    WHERE       ur.user_id = '$userid'
)
OR      `id` IN (
    SELECT      `perm_id` 
    FROM        `permissions_user` 
    WHERE       `user_id` = '$userid'
)

$userid 是当前用户的用户表中的 id。我将结果中的权限名称存储到一个数组中,该数组表示根据他/她的角色他/她的 id 分配给用户的所有权限:

<?php
$user_perms = array();
if(mysql_num_rows($query) > 0):
    while($result = mysql_fetch_array($query):
        $user_perms[] = $result('name');
    endwhile;
endif;
?>

print_r($user_perms); 产生如下输出:

Array ( 
    [0] => ACCESS_TELEPHONELIST_PAGE
    [1] => ACCESS_VACATIONSCHED_PAGE
    [2] => ACCESS_TOURSCHED_PAGE
    [3] => ACCESS_WORKSCHED_PAGE
    [4] => ACCESS_RESOURCES_LINKS
    [5] => ACCESS_PMTOOL_PAGE
    [6] => ACCESS_TOOLSTOOL_PAGE
    [7] => ACCESS_SHOPTOOLLIST_PAGE
    [8] => ACCESS_TOOLINVENTORY_PAGE
    [9] => ACCESS_MANAGETOOLLIST_PAGE
    [10] => ACCESS_TOOLREPORTS_PAGE
    [11] => ACCESS_JOBSLIST_LINKS
    [12] => MAIN_TAB_TOOLSTOOL
    [13] => ADMIN_TAB_PODMANAGEMENT
    [14] => TOOL_TAB_SHOPTOOLLIST
)

我想要做的是将所有用户的角色存储到另一个数组中,而无需进行第二次查询。我认为我需要为子查询使用别名,所以我尝试了这个查询:

SELECT      permissions.*, usersroles.role_id 
FROM        `permissions`
INNER JOIN  (
    SELECT      ur.user_id, pr.perm_id, ur.role_id
    FROM        `user_roles` as ur
    LEFT JOIN   `permissions_role` as pr 
    ON          ur.role_id = pr.role_id
    WHERE       ur.user_id = '$userid'
) AS usersroles ON usersroles.perm_id = permissions.id
INNER JOIN  (
    SELECT      `perm_id`, `user_id` 
    FROM        `permissions_user` 
    WHERE       `user_id` = '$userid'
) AS userperms ON userperms.user_id = usersroles.user_id
              AND userperms.perm_id = permissions.id

并且,使用类似于上述代码的代码...

<?php
$user_perms = array();
$user_roles = array();
if(mysql_num_rows($query) > 0):
    while($result = mysql_fetch_array($query):
        $user_perms = $result('name');
        $user_roles = $result('role_id');
    endwhile;
endif;
?>

...我收到此警告:

Warning: mysql_num_rows() expects parameter 1 to be resource, boolean given

但是,我想print_r($user_roles); 并生成如下所示的输出:

Array (
    [0] => administrator
    [1] => humanresources
    [2] => podmanager
)

谁能告诉我我做错了什么,或者建议一种更好的方法来将我需要的数据从 one 查询中获取到 2 数组中?

编辑:经过仔细考虑,我将代码更改为使用 ImreL 建议的 2 个查询。生成的代码运行良好且执行迅速。我已经编辑了答案以显示我使用的最终代码并添加了支持代码以演示我如何使用这 2 个查询。非常感谢 ImreL!

【问题讨论】:

  • 漂亮的图表 :) 你用什么工具来创建它?
  • 我使用 Microsoft Access... 创建了表格,设置了关系,然后进入设计视图并使用我的图形程序截取了屏幕截图。
  • @Neil 感谢您编辑我的帖子并添加图表! :)
  • 您的 SQL 语法可能有错误。尝试在mysql_query 调用之后添加or die(mysql_error());,例如mysql_query("SELECT * FROM table") or die(mysql_error()); 作为一种快速查看查询问题的方法。我不能立即发现任何问题,但是这里已经很晚了 :) 顺便说一句,你为什么不能使用两个查询?如果我的理解正确,您似乎在这里追求的是两件完全不同的事情,因为并非所有权限都与角色 ID 相关联,所以无论如何您都会有一些 NULL。
  • @AmyMcCrobie:需要明确的是,您正在尝试获取两个完全独立且未连接的数组 - 当前用户拥有的每个权限之一(无论是授予他们还是授予他们的角色之一),并且分别,他们拥有的每个角色之一?您不想了解哪些许可来自哪里?对吗?

标签: php mysql subquery left-join inner-join


【解决方案1】:

编辑:这是最终的工作查询。另外,我认为查看查询的使用方式可能对其他人有用,因此包含了完整的说明。

//build the array(s)
$userperms = array();
$userroles = array();
if(isset($_SESSION['userid'])):
    //get the userid
    $thisuserid = $_SESSION['userid'];
    //get the permissions for each of the user's roles
    $Cpermissions_role = mysql_query(" 
    SELECT      r.type, ur.user_id, pr.perm_id, ur.role_id, p.name
    FROM        user_roles ur
    LEFT JOIN   permissions_role pr ON ur.role_id = pr.role_id
    LEFT JOIN   roles r ON ur.role_id = r.id
    LEFT JOIN   permissions p ON p.id = pr.perm_id
    WHERE       ur.user_id = '$userid'") or die(mysql_error());
    //get the extra permissions for the user
    $Cpermissions_user = mysql_query("
    SELECT      pu.user_id, pu.perm_id, p.name
    FROM        permissions_user pu
    LEFT JOIN   permissions p ON p.id = pu.perm_id
    WHERE       pu.user_id = '$userid'") or die(mysql_error());
    //build an array of the user's roles & an array of the user's permissions
    if(mysql_num_rows($Cpermissions_role) > 0):
        while($Rpermissions_role = mysql_fetch_array($Cpermissions_role)):
            if(empty($userperms)):
                $userperms[] = $Rpermissions_role['name'];
            elseif(!in_array($Rpermissions_role['name'],$userperms)):
                $userperms[] = $Rpermissions_role['name'];
            endif;
            if(empty($userroles)):
                $userroles[] = $Rpermissions_role['type'];
            elseif(!in_array($Rpermissions_role['type'],$userroles)):
                $userroles[] = $Rpermissions_role['type'];
            endif;
        endwhile;
    endif;
    if(mysql_num_rows($Cpermissions_user) > 0):
        while($Rpermissions_user = mysql_fetch_array($Cpermissions_user)):
            if(empty($userperms)):
                $userperms[] = $Rpermissions_user['name'];
            elseif(!in_array($Rpermissions_user['name'],$userperms)):
                $userperms[] = $Rpermissions_user['name'];
            endif;
        endwhile;
    endif;
endif;
/**
 * Determines if the user has permission for the page or parts of page
 * @param string $perm the permission constant
 * @return boolean true if user has access, false if not
 */
function hasPermission($perm){
    global $userperms;
    if(empty($userperms)):
        return false;
    else:
        if(is_array($userperms)):
            if(in_array($perm,$userperms)):
                return true;
            else:
                return false;
            endif;
        else:
            if($perm == $userperms):
                return true;
            else:
                return false;
            endif;
        endif;
    endif;
}

然后,我使用以下决策语句:

if(hasPermission("ACCESS_HOME_PAGE")):
    //perform circus tricks here
endif;

所以,现在print_r($userperms); 输出这个:

Array ( 
    [0] => ACCESS_TELEPHONELIST_PAGE
    [1] => ACCESS_VACATIONSCHED_PAGE
    [2] => ACCESS_TOURSCHED_PAGE
    [3] => ACCESS_WORKSCHED_PAGE
    [4] => ACCESS_RESOURCES_LINKS
    [5] => ACCESS_PMTOOL_PAGE
    [6] => ACCESS_TOOLSTOOL_PAGE
    [7] => ACCESS_SHOPTOOLLIST_PAGE
    [8] => ACCESS_TOOLINVENTORY_PAGE
    [9] => ACCESS_MANAGETOOLLIST_PAGE
    [10] => ACCESS_TOOLREPORTS_PAGE
    [11] => ACCESS_JOBSLIST_LINKS
    [12] => MAIN_TAB_TOOLSTOOL
    [13] => ADMIN_TAB_PODMANAGEMENT
    [14] => TOOL_TAB_SHOPTOOLLIST
    [15] => ACCESS_HOME_PAGE
)

而且,print_r($userroles); 输出如下:

Array ( 
    [0] => administrator
    [1] => humanresourcees
    [2] => podmanager
)

而且,为了彻底,这里是我用来访问 php 数组的 JavaScript:

var js_userperms = new Array();
js_userperms = ["<?php echo join("\", \"", $userperms); ?>"];

还有,jQuery 决策声明:

if(jQuery.inArray("ACCESS_HOME_PAGE", js_userperms) != -1){
    //perform circus tricks here
}

【讨论】:

  • 由于查询严重错误而被否决。您暂时已将其删除。
  • 我试着回答了这个问题。由于您在几个小时后接受了自己的答案,因此我不想贡献更多时间。干杯。
【解决方案2】:

查询需要在用户加载的每个页面上运行,我们 拥有超过 30,000 个权限和 3,000 个角色。我只是在尝试非常 很难将我的查询数量保持在最低限度。我们还举办我们的 7 我们办公室服务器上的站点,而服务器似乎不是 能够处理我们产生的流量(不幸的是,我 无法控制)

我看到你的意图是好的,但让我告诉你:

“查询次数”不是衡量您网站性能的正确方法。

很多时候 2 个简单查询比 1 个复杂查询使用更少的资源。

还有其他方法可以加速您的网站:

  • 评估您是否真的需要在每个请求上加载所有这些角色和权限。也许只查询所需的角色/权限就足够了。
  • 有适当的索引
  • 利用缓存技术减少负载(网站内容)

所以最后,我尝试提出查询来满足所要求的内容:

select * from (
    select ur.role_id, p.*
    from user_roles ur 
    left join permissions_role pr on ur.role_id =  pr.role_id
    left join permissions p on p.id = pr.perm_id
    where ur.user_id = '$userid'
  union all
    select null as role_id, p.*
    from permissions_user pu
    join permissions p on p.id = pu.perm_id
    where pu.user_id = '$userid'
) sub
group by ifnull(name,role_id) -- group by to eliminate duplicates

但这对性能不好。 您最好使用 2 个查询:第一个获得用户的所有权限

select p.* from permissions p
join permissions_role pr on pr.perm_id = p.id
join user_roles ur on ur.role_id =  pr.role_id and ur.user_id = '$userid'
union
select p.* from permissions p
join  permissions_user pu on pu.perm_id = p.id and pu.user_id = '$userid';

第二次获得所有角色。

【讨论】:

  • 感谢您的帮助。仅供查看该问题的其他人参考...我还发布了一个答案,该答案反映了最终的工作查询和支持代码,以演示如何使用查询。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多