2010-06-26 97 views
3

目前,我有以下SQL硬编码的视图:动态MySQL查询/视图交叉

select username 
    ,(case user_role.role_id when 1 then true else false end) as ROLE_SUPER 
    ,(case user_role.role_id when 2 then true else false end) as ROLE_ADMIN 
    ,(case user_role.role_id when 3 then true else false end) as ROLE_VIEW 
    ,(case user_role.role_id when 4 then true else false end) as ROLE_USER 
    ,(case user_role.role_id when 5 then true else false end) as ROLE_EMAIL 
    from user 
    left outer join user_role on user.id=user_role.user_id 
    left outer join role on user_role.role_id = role.id; 

我的问题是,它是否可以动态生成中的作用从表中记录的角色列。

回答

8

可以做你想做的事,但我不确定为什么你会想。一旦你有你的动态列别名,你如何计划引用它们?也就是说,如果您从数据库中提取列别名,那么您将如何使用它们?我可能会错过你的问题背后的原因。

反正我假设你有一个这样的结构:

CREATE TABLE `user` (
    `id` int(11) NOT NULL auto_increment, 
    `username` varchar(255) default NULL, 
    PRIMARY KEY (`id`) 
); 

CREATE TABLE `role` (
    `id` int(11) NOT NULL auto_increment, 
    `role` varchar(255) default NULL, 
    PRIMARY KEY (`id`) 
); 

CREATE TABLE `user_role` (
    `user_id` int(11), 
    `role_id` int(11), 
    PRIMARY KEY (`user_id`, `role_id`) 
); 

INSERT INTO `user` (`username`) VALUES 
    ('Bob'), ('Alice'), ('Carol'), ('Dave'), ('Eve'); 

INSERT INTO `role` (`role`) VALUES 
    ('Super'), ('Admin'), ('View'), ('User'), ('Email'); 

INSERT INTO `user_role` VALUES 
    (1,1), (2,2), (3,3), (4,4), (5,5); 

从这一点,你可以获取有关用户和他们的角色(一个或多个)信息:

SELECT username, role.id AS role_id, role.role AS role FROM user_role 
JOIN user ON user.id = user_role.user_id 
JOIN role ON role.id = user_role.role_id; 

+----------+---------+-------+ 
| username | role_id | role | 
+----------+---------+-------+ 
| Bob  |  1 | Super | 
| Alice |  2 | Admin | 
| Carol |  3 | View | 
| Dave  |  4 | User | 
| Eve  |  5 | Email | 
+----------+---------+-------+ 

您还可以创建一个列别名为特定角色:

SELECT username, (role.id = 1) AS Super FROM user_role 
JOIN user ON user.id = user_role.user_id 
JOIN role ON role.id = user_role.role_id; 

+----------+-------+ 
| username | Super | 
+----------+-------+ 
| Bob  |  1 | 
| Alice |  0 | 
| Carol |  0 | 
| Dave  |  0 | 
| Eve  |  0 | 
+----------+-------+ 

但是,如果我正确理解您的问题,你想要做的是从角色名称中生成列别名。你不能用一个变量作为一个MySQL声明列别名,但你可以构造一个事先准备好的声明:

SET @sql = (SELECT CONCAT(
    'SELECT username, ', 
    GROUP_CONCAT('(role.id = ', id, ') AS ', role SEPARATOR ', '), 
    ' FROM user_role ', 
    'JOIN user ON user.id = user_role.user_id ', 
    'JOIN role ON role.id = user_role.role_id;') 
FROM role); 

SELECT @sql; 

+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
| @sql                                                         | 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
| SELECT username, (role.id = 1) AS Super, (role.id = 2) AS Admin, (role.id = 3) AS View, (role.id = 4) AS User, (role.id = 5) AS Email FROM user_role JOIN user ON user.id = user_role.user_id JOIN role ON role.id = user_role.role_id; | 
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 

,你从输出,其生成包含SQL SELECT语句的字符串见。现在,您需要创建从该字符串一份声明,并执行结果:

PREPARE stmt FROM @sql; 
EXECUTE stmt; 
+----------+-------+-------+------+------+-------+ 
| username | Super | Admin | View | User | Email | 
+----------+-------+-------+------+------+-------+ 
| Bob  |  1 |  0 | 0 | 0 |  0 | 
| Alice |  0 |  1 | 0 | 0 |  0 | 
| Carol |  0 |  0 | 1 | 0 |  0 | 
| Dave  |  0 |  0 | 0 | 1 |  0 | 
| Eve  |  0 |  0 | 0 | 0 |  1 | 
+----------+-------+-------+------+------+-------+ 

编辑

为了调用交叉表查询更方便,你可以换了整个事情了在存储过程。在以下示例中,我无法使GROUP_CONCATSET @sql声明中工作,如上所述。相反,我必须把它分解成它自己的变量。我不知道为什么没有工作,但最终的结果是一样的,而且代码可能少一点神秘:

DELIMITER // 
DROP PROCEDURE IF EXISTS test.crosstab// 
CREATE PROCEDURE test.crosstab() 
BEGIN 
    SET @cols = (SELECT GROUP_CONCAT(
     '(role.id = ', id, ') AS ', role 
     SEPARATOR ', ') FROM role); 
    SET @sql = CONCAT(
     'SELECT username, ', 
     @cols, 
     ' FROM user_role ', 
     'JOIN user ON user.id = user_role.user_id ', 
     'JOIN role ON role.id = user_role.role_id;'); 
    PREPARE stmt FROM @sql; 
    EXECUTE stmt; 
END; 
// 
DELIMITER ; 

CALL test.crosstab(); 

+----------+-------+-------+------+------+-------+ 
| username | Super | Admin | View | User | Email | 
+----------+-------+-------+------+------+-------+ 
| Bob  |  1 |  0 | 0 | 0 |  0 | 
| Alice |  0 |  1 | 0 | 0 |  0 | 
| Carol |  0 |  0 | 1 | 0 |  0 | 
| Dave  |  0 |  0 | 0 | 1 |  0 | 
| Eve  |  0 |  0 | 0 | 0 |  1 | 
+----------+-------+-------+------+------+-------+ 
+0

因为某些原因我不能让准备好的语句返回任何东西,但空在MySQL 5.1上,但我确实认为核心指令是有意义的,我将继续努力使其发挥作用。你曾问过我如何参考这个实现的角色。我的过程将钩在蚂蚁的生成例程,这将生成所需的.java以及映射文件。如果没有重新启动,这些角色不会改变,但是这样做可以方便未来增加角色。目前我使用上面的硬编码版本来提供用户可以与之交互的网格。 – ebt 2010-06-29 01:53:05

+0

并感谢这很好的答案。 – ebt 2010-06-29 01:54:42

+0

@ebt:我在Debian上运行MySQL 5.0.51a-24 + lenny3,并且准备好的语句正常工作(以上示例直接从我的控制台复制并粘贴)。我刚刚在运行MySQL 5.1.47的虚拟主机服务器上尝试了上述示例,并且他们也在那里工作。我已经编辑了包含存储过程的答案,该存储过程将命令包装成一个很好的小包。这是不可能的,但也许这(或分开'GROUP_CONCAT'调用)将解决您的问题。 – Mike 2010-06-29 07:20:52