【发布时间】:2016-11-21 14:28:32
【问题描述】:
我们目前有一个包含 90 列的表,随着表的增长和业务需求的变化,我们不得不大量更改表(添加/删除列和索引)。
|------ (Table name: quotes)
|Column|Type|Null|Default
|------
|//**id**//|int(11)|No|
....
|completed_at|datetime|Yes|NULL
|reviewed_at|datetime|Yes|NULL
|marked_dud_at|datetime|Yes|NULL
|closed_at|datetime|Yes|NULL
|subscribed_at|datetime|Yes|NULL
|admin_checked_at|datetime|Yes|NULL
|priced_at|datetime|Yes|NULL
|number_verified_at|datetime|Yes|NULL
|created_at|datetime|Yes|NULL
|deleted_at|datetime|Yes|NULL
对于申请,我们的工作人员不断查询上述数据的各种变化,例如已完成(completed_at)、已检查(admin_checked_at)和未删除、已审核(deleted_at、review_at)的位置
我们认为将其中一些列卸载到自己的行中可能会更容易,我们将其称为quotes_actions,然后在查询时进行一些连接。
|------ (Table name: quotes_actions)
|Column|Type|Null|Default
|------
|//**id**//|int(11)|No|
|quote_id|int(11)|No|
|action|varchar(100)|No|
|user_id|int(11)|No|
|time|datetime|Yes|NULL
|created_at|datetime|Yes|NULL
一个例子是 action = 'completed' 使用字段,索引覆盖 quote_id 和 action。
我们已经在 150,000 行上将数据拆分为这种格式,它并不比使用正确索引查询原始数据库快也不慢。
有没有人有这方面的经验并对每种方法有任何建议或陷阱?添加覆盖索引并根据需要向原始表添加列需要花费大量时间,而第二种方法已设置好索引,但引入了更多的连接和更复杂的查询。
0.09s
select * from `quotes`
where `completed_at` is not null
and `approved_at` is not null
and deleted_at is null
=>
0.0005s
select * from `quotes_new`
inner join quotes_actions as q1 on q1.action = 'completed' and q1.quote_id = quotes_new.id
inner join quotes_actions as q2 on q2.action = 'approved' and q2.quote_id = quotes_new.id
where quotes_new.deleted_at is null
此外,如果第二种方法更好,如果报价未获批准,您如何查询否定结果?
【问题讨论】:
-
停止所有这些改动。与团队坐下来,弄清楚如何规范您的数据库。
-
遵循 3 个 NF 来规范化您的数据库,而不是形成这种尴尬的解决方案
-
不确定这是否真的是一个规范化问题。您的第一个表没有显示规范化问题 IMOH。这里的问题更多的是一个不成熟的产品。与团队进行头脑风暴可能确实有帮助。但是看到“quotes_action”列的数量,并考虑到您定期添加越来越多的列,为清楚起见,我将采用第二个解决方案。您仍然可以制作一些 VIEWS 来恢复原始表格布局并方便查询。
-
其中一些是连续的吗?重要的是“状态”——创建,然后审查,然后定价,然后完成?如果是这样,您可能需要将“状态”记录为单个有序数字并放弃日期。
-
你的选项 2 有点 EAV 的味道,太糟糕了。
标签: mysql indexing database-normalization