我正在使用自定义用户设置功能更新我的用户管理系统。换句话说,我想在数据库中保存自定义用户设置/选项。最初我考虑序列化数据并将其保存到 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 |
+---------+-----------------------------+------------------+
有没有更好的方法来存储这些数据?
优点:
- 能够搜索自定义选项,例如列出所有开发人员和具有 API 访问权限的用户。
- 能够只加载一个选项组。
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 不适用于多个表,所以我想这使得进一步规范化变得无关紧要.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)
注意:
- 将数据存储在 NOSQL 数据库中不是一种选择。
- 实际上,这更像是一个学术问题,因为实际存储的自定义数据非常小,而且对于极少数用户来说也是如此。因此,在我的特定情况下,将其存储为序列化数组或加载整个数组不会产生巨大的开销。
最佳答案
更新(见下方之前的版本):
您的解决方案和我以前的版本实际上都存在一个问题,即并非您的数组结构中的所有信息都被保存。您仍然可以通过将选项名称的最后一个元素放在单独的字段中来避免 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 |
+----------+-----------------------------+------------------+
第二个表包含由一组值组成的所有选项(没有重复;* 标记关键列):
+----------+-----------------------------+------------------+
| 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 |
+----------+-----------------------------+------------------+---------+
关于mysql - 在规范化表中保存数组数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20297487/