2012-08-06 138 views
1

這非常迅速複雜化,我開始質疑數據庫設計。 應用程序的基本概念是:MySQL使用A表的行值連接三個表格

  1. 用戶帳戶
  2. 特點
  3. 訪問級別

因此,用戶必須爲每個功能不同的訪問級別。我會想到的是相當基本和普遍的應用。

模式:

CREATE TABLE `user_accounts` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_login` varchar(100) COLLATE utf8_unicode_ci NOT NULL, 
    `user_password` varchar(60) COLLATE utf8_unicode_ci NOT NULL, 
    `user_fname` varchar(100) COLLATE utf8_unicode_ci NOT NULL, 
    `user_lname` varchar(100) COLLATE utf8_unicode_ci NOT NULL, 
    `user_group` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'Default', 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `user_login` (`user_login`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ; 

INSERT INTO `user_accounts` VALUES(1, '[email protected]', 'secret', 'Example', 'Name', 'Admin'); 
INSERT INTO `user_accounts` VALUES(2, '[email protected]', 'secret', 'John', 'Doe', 'Trainer'); 
INSERT INTO `user_accounts` VALUES(3, '[email protected]', 'secret', 'Jane', 'Doe', 'Default'); 

CREATE TABLE `user_access_meta` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `type` varchar(50) COLLATE utf8_unicode_ci NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `type` (`type`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

INSERT INTO `user_access_meta` VALUES(1, 'type_1'); 
INSERT INTO `user_access_meta` VALUES(2, 'type_2'); 
INSERT INTO `user_access_meta` VALUES(3, 'type_3'); 
INSERT INTO `user_access_meta` VALUES(4, 'type_4'); 
INSERT INTO `user_access_meta` VALUES(5, 'type_5'); 
INSERT INTO `user_access_meta` VALUES(6, 'type_6'); 

CREATE TABLE `user_access_levels` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_login` varchar(100) COLLATE utf8_unicode_ci NOT NULL, 
    `type` varchar(50) COLLATE utf8_unicode_ci NOT NULL, 
    `level` int(1) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `user_login_2` (`user_login`,`type`), 
    KEY `user_login` (`user_login`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ; 

INSERT INTO `user_access_levels` VALUES(1, '[email protected]', 'type_1', 1); 
INSERT INTO `user_access_levels` VALUES(2, '[email protected]', 'type_2', 1); 
INSERT INTO `user_access_levels` VALUES(3, '[email protected]', 'type_3', 0); 
INSERT INTO `user_access_levels` VALUES(4, '[email protected]', 'type_5', 2); 
INSERT INTO `user_access_levels` VALUES(5, '[email protected]', 'type_2', 1); 
INSERT INTO `user_access_levels` VALUES(6, '[email protected]', 'type_3', 1); 
INSERT INTO `user_access_levels` VALUES(7, '[email protected]', 'type_5', 3); 
INSERT INTO `user_access_levels` VALUES(8, '[email protected]', 'type_4', 1); 

這些表其實有很多的字段,並在它們之間具有外鍵約束,但我已經條紋下來的這個例子。它們也被單獨用於其他目的。

我已經成功地能夠加入所有三個表一起與此單個用戶:

SELECT 
ua.`user_fname`, 
uam.`type`, 
ual.`level` 
FROM `user_access_meta` uam 
LEFT JOIN `user_access_levels` ual 
ON ual.`user_login` = '[email protected]' 
AND uam.`type` = ual.`type` 
JOIN `user_accounts` ua 
ON ua.`user_login` = '[email protected]'; 

輸出:

| USER_FNAME | TYPE | LEVEL | 
-------------------------------- 
| Example | type_1 |  1 | 
| Example | type_2 |  1 | 
| Example | type_3 |  0 | 
| Example | type_4 | (null) | 
| Example | type_5 |  2 | 
| Example | type_6 | (null) | 

即使這是不理想的,但是這就是我可以拿出來,它的目的。


現在,我需要做的是選擇所有用戶,包括他們的訪問級別。這將是這個樣子:

| USER_FNAME | type_1 | type_2 | type_3 | type_4 | type_5 | type_6 | 
-------------------------------------------------------------------------- 
| Example |  1 |  1 |  0 | (null) |  2 | (null) | 
|  John | (null) |  1 |  1 | (null) |  3 | (null) | 
|  Jane | (null) | (null) | (null) |  1 | (null) | (null) | 

我覺得這可能不是最好的設計,但我有這樣的設計去的原因是,我可以輕鬆地添加和刪除功能,甚至暫時單獨禁用它們。

設計應該重新考慮嗎?用這種設計能夠獲得我期待的結果嗎?

我已經把它放在SQL小提琴上。 http://sqlfiddle.com/#!2/bb313/2/0

回答

0

雖然我不熟悉MySQL的具體細節,但在我看來,您正在描述一個數據透視表查詢的基本示例。你在尋找什麼對我來說似乎是合理的,所以我不認爲根據你在這裏展示的內容,我會太在意重新訪問數據模型。你可能會發現把「等級」放回到「類型」表,基於醇「鋸」正常化直到打擊傷害,反正常化,直到它工作:)「

只是我的0.02美元。祝你好運。

0

我通常使用BIGINT列並使用位掩碼來設置值。

例如1級= 2,級別2 = 4,級別3 = 8,級別4 = 16,等。

給某人Level1和Level2訪問:

update user set access_level = 2 & 4 

是否有人有level2的訪問?

select 1 from user where access_level | 2 AND user_id = ? 
1

我對你的桌子設計有一些建議,然後如何獲得你想要的格式的數據。

首先在數據庫設計上,我建議的更改在表user_access_levels中。改變你的表到以下幾點:

CREATE TABLE `user_access_levels` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_id` int(11) NOT NULL, 
    `type_id` int(11) NOT NULL, 
    `level` int(1) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `user_id_2` (`user_id`,`type_id`), 
    KEY `user_id` (`user_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ; 

沒有必要存儲user_logintype此表時,你可以儲存user_idtype_id。將這兩個作爲外鍵用於它們各自的表格。

然後以您想要的格式獲取數據。 MySQL沒有PIVOT函數,因此您將要使用具有聚合函數的CASE語句。

select ua.user_fname, 
    MIN(CASE WHEN uam.type = 'type_1' THEN ual.level END) type_1, 
    MIN(CASE WHEN uam.type = 'type_2' THEN ual.level END) type_2, 
    MIN(CASE WHEN uam.type = 'type_3' THEN ual.level END) type_3, 
    MIN(CASE WHEN uam.type = 'type_4' THEN ual.level END) type_4, 
    MIN(CASE WHEN uam.type = 'type_5' THEN ual.level END) type_5, 
    MIN(CASE WHEN uam.type = 'type_6' THEN ual.level END) type_6 
FROM user_accounts ua 
LEFT JOIN user_access_levels ual 
    ON ua.id = ual.user_id 
LEFT JOIN user_access_meta uam 
    ON ual.type_id = uam.id 
group by ua.user_fname 

見,如果你提前知道時間,你想要得到的值類型列的SQL Fiddle with a Demo

這個版本將工作。但是,如果它是未知的,那麼你可以使用準備好的語句來動態生成它。

下面是該查詢的版本使用預處理語句:

SET @sql = NULL; 
SELECT 
    GROUP_CONCAT(DISTINCT 
    CONCAT(
     'MIN(case when type = ''', 
     type, 
     ''' then level end) AS ', 
     replace(type, ' ', '') 
    ) 
) INTO @sql 
FROM user_access_meta; 

SET @sql = CONCAT('SELECT ua.user_fname, ', @sql, ' FROM user_accounts ua 
LEFT JOIN user_access_levels ual 
    ON ua.id = ual.user_id 
LEFT JOIN user_access_meta uam 
    ON ual.type_id = uam.id 
group by ua.user_fname'); 

PREPARE stmt FROM @sql; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 

看到一個SQL Fiddle with Demo

+0

感謝您對這樣一個偉大的答案!我相信第二個版本會起作用,但是當我在phpMyAdmin中運行查詢時,我得到'給予EXECUTE'的未知準備好的語句處理程序(stmt),我只是不熟悉準備好的語句,知道這意味着什麼。我已經做了一些研究,但是我覺得我可能會走錯路,因爲我會從PHP查詢數據庫。當我從PHP(PDO)調用它時,我從'fetchAll()'得到一個空數組,並且沒有錯誤。感謝您的回答,這非常有幫助,我學到了很多東西。 – JDavis 2012-08-23 00:43:16

+0

@JDavis不幸的是我現在沒有MySQL的副本。我會看看我能否弄清楚。 – Taryn 2012-08-23 00:54:09