2010-06-26 70 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