这很快变得复杂,我开始质疑数据库设计。 该应用程序的基本概念是:
因此,用户对每个功能都有不同的访问级别。我想是相当基本和普遍的应用。
架构:
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@example.com', 'secret', 'Example', 'Name', 'Admin');
INSERT INTO `user_accounts` VALUES(2, 'john@example.com', 'secret', 'John', 'Doe', 'Trainer');
INSERT INTO `user_accounts` VALUES(3, 'jane@example.com', '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@example.com', 'type_1', 1);
INSERT INTO `user_access_levels` VALUES(2, 'email@example.com', 'type_2', 1);
INSERT INTO `user_access_levels` VALUES(3, 'email@example.com', 'type_3', 0);
INSERT INTO `user_access_levels` VALUES(4, 'email@example.com', 'type_5', 2);
INSERT INTO `user_access_levels` VALUES(5, 'john@example.com', 'type_2', 1);
INSERT INTO `user_access_levels` VALUES(6, 'john@example.com', 'type_3', 1);
INSERT INTO `user_access_levels` VALUES(7, 'john@example.com', 'type_5', 3);
INSERT INTO `user_access_levels` VALUES(8, 'jane@example.com', '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@example.com'
AND uam.`type` = ual.`type`
JOIN `user_accounts` ua
ON ua.`user_login` = 'email@example.com';
输出:
| 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 Fiddle上。 http://sqlfiddle.com/#!2/bb313/2/0
答案 0 :(得分: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_login
和type
,则无需在此表格中存储user_id
和type_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
如果您提前知道要获取值的类型列,则此版本将起作用。但是如果它是未知的,那么你可以使用预处理语句动态生成它。
以下是使用预准备语句的查询版本:
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;
答案 1 :(得分:0)
虽然我不熟悉MySQL的细节,但在我看来,你正在描述一个非常基本的数据透视表查询示例。您正在寻找的内容对我来说似乎是合理的,所以我不认为根据您在此处显示的内容,我会过于担心重新访问数据模型。您可能会发现将“级别”放回“类型”表,基于ol'锯“Normalize til hit hurts,denormalize直到它工作:)”
只需0.02美元。祝你好运。
答案 2 :(得分:0)
我通常使用BIGINT列并使用位屏蔽来设置值。
例如level1 = 2,level2 = 4,level3 = 8,level4 = 16等。
授予某人level1和level2访问权限:
update user set access_level = 2 & 4
有人有二级访问权限吗?
select 1 from user where access_level | 2 AND user_id = ?