我正在使用自定义用户设置功能更新我的用户管理系统。换句话说,我想在数据库中保存自定义用户设置/选项。最初我考虑序列化数据并将其保存到TEXT
字段。但这具有不能通过配置值进行搜索的缺点。
因此我决定将数据规范化并存储在不同的表中。由于配置数据是多维的,我不得不压扁数据。例如,
Array (
[user] => Array (
[is_developer] => 1
[api_access] => 1
)
[api] => Array (
[scopes] => Array (
[0] => astro
[1] => user_info
)
[default_scope] => user_info
)
[astro] => Array (
[location_id] => 12345
[timezone] => America/New_York
[coordinates] => Array (
[latitude] => 12.22
[longitude] => 24.44
)
)
)
转换为
+---------+-----------------------------+------------------+
| user_id | config | value |
+---------+-----------------------------+------------------+
| 1 | user.is_developer | 1 |
| 1 | user.api_access | 1 |
| 1 | api.scopes | astro |
| 1 | api.scopes | user_info |
| 1 | api.default_scope | user_info |
| 1 | astro.location_id | 12345 |
| 1 | astro.timezone | America/New_York |
| 1 | astro.coordinates.latitude | 12.22 |
| 1 | astro.coordinates.longitude | 24.44 |
+---------+-----------------------------+------------------+
有没有更好的方法来存储这些数据?
优点:
SELECT * FROM user_config WHERE config LIKE 'astro.%' AND user_id = 1;
缺点:
api.scopes.0
,api.scopes.1
等),那么优势#1将部分丢失。INSERT ... ON DUPLICATE UPDATE
查询。相反,当需要更新数据时,我必须删除现有值并在事务中重新插入数据。更新
在阅读了unique2的答案后,我又得到了一个想法。进一步规范化数据可能会解决唯一的关键问题。即,将密钥保存在一个表中,将数据保存在另一个表中。 需要检查 插入不适用于多个表,因此我认为这会使进一步的规范化无关紧要。 INSERT ... ON DUPLICATE UPDATE
是否适用于多个表。
mysql> SELECT * FROM user_config;
+-----------+---------+-----------------------------+
| config_id | user_id | config |
+-----------+---------+-----------------------------+
| 1 | 1 | api.default_scope |
| 2 | 1 | api.scopes |
| 3 | 1 | astro.coordinates.latitude |
| 4 | 1 | astro.coordinates.longitude |
| 5 | 1 | astro.location_id |
| 6 | 1 | astro.timezone |
| 7 | 1 | user.api_access |
| 8 | 1 | user.is_developer |
+-----------+---------+-----------------------------+
8 rows in set (0.00 sec)
mysql> SELECT * FROM user_config_value;
+-----------+------------------+
| config_id | value |
+-----------+------------------+
| 1 | user_info |
| 2 | astro |
| 2 | user_info |
| 3 | 12.22 |
| 4 | 24.44 |
| 5 | 12345 |
| 6 | America/New_York |
| 7 | 1 |
| 8 | 1 |
+-----------+------------------+
9 rows in set (0.00 sec)
注意:
答案 0 :(得分:1)
更新(请参阅下面的上一版本):
使用您的解决方案和我以前的版本实际上存在的问题是并非保存了阵列结构中的所有信息。您仍然可以通过将选项名称的最后一个元素放在单独的字段中来避免api.scopes.1
。如果将此与软删除相结合,则可以使用INSERT ... ON DUBLICATE UPDATE
。
+----------+-------------------+---------------+------------------+---------+
| user_id* | config_group* | config* | value | deleted |
+----------+-------------------+---------------+------------------+---------+
| 1 | user | is_developer | 1 | 0 |
| 1 | user | api_access | 1 | 0 |
| 1 | api.scopes | 1 | astro | 0 |
| 1 | api.scopes | 2 | user_info | 0 |
| 1 | api | default_scope | user_info | 0 |
| 1 | astro | location_id | 12345 | 0 |
| 1 | astro | timezone | America/New_York | 0 |
| 1 | astro.coordinates | latitude | 12.22 | 0 |
| 1 | astro.coordinates | longitude | 24.44 | 0 |
+----------+-----------------------------------+------------------+---------+
* marks key columns
以前的版本:
如果您将数据拆分为两个表,则可以为每个表使用唯一键。
第一个表包含所有采用单个值的配置选项(*标记键列):
+----------+-----------------------------+------------------+
| user_id* | config* | value |
+----------+-----------------------------+------------------+
| 1 | user.is_developer | 1 |
| 1 | user.api_access | 1 |
| 1 | api.default_scope | user_info |
| 1 | astro.location_id | 12345 |
| 1 | astro.timezone | America/New_York |
| 1 | astro.coordinates.latitude | 12.22 |
| 1 | astro.coordinates.longitude | 24.44 |
+----------+-----------------------------+------------------+
第二个表包含由一组值组成的所有选项(没有dublicates; *标记键列):
+----------+-----------------------------+------------------+
| user_id* | config* | value* |
+----------+-----------------------------+------------------+
| 1 | api.scopes | astro |
| 1 | api.scopes | user_info |
+----------+-----------------------------+------------------+
因此,您可以使用数据库来确保数据完整性。 INSERT ... ON DUBLICATE UPDATE
自然地与第一个表一起工作。如果您愿意使用软删除,也可以将它与第二个表一起使用:
+----------+-----------------------------+------------------+---------+
| user_id* | config* | value* | deleted |
+----------+-----------------------------+------------------+---------+
| 1 | api.scopes | astro | 0 |
| 1 | api.scopes | user_info | 0 |
| 1 | api.scopes | old | 1 |
+----------+-----------------------------+------------------+---------+
答案 1 :(得分:0)
您重新设计了一个名为Entity-Attribute-Value的设计。这是不标准化意味着什么。在你的问题中提出的想法,以及@ unique2的答案中提出的解决方案,都不符合正常形式的任何定义 - 它们甚至不是关系。
你是对的,唯一的约束不起作用。外键约束,甚至NOT NULL都没有。例如,您如何使数据库强制执行user.api_access
的强制值?
有关为什么EAV是非关系型设计的更多讨论,请参阅我的博客EAV FAIL或许多my answers on StackOverflow的entity-attribute-value个问题。
有关支持用户定义属性的替代解决方案,请参阅我的演示文稿Extensible Data Modeling。我概述了以下的优点和缺点: