mysql - MYSQL DB规范化和查询索引

标签 mysql indexing database-normalization

当前,我们有一个包含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,reviewed_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。

我们已经将数据分割成15万行的这种格式,它的速度和速度都没有比使用正确的索引查询原始数据库快或慢。

有没有人对此有任何经验,对每种方法都有建议或陷阱?添加覆盖索引并根据需要在原始表中添加列需要花费大量时间,而第二种方法是准备好建立索引,但是引入了更多的联接和更复杂的查询。

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


此外,如果第二种方法更好,那么如何查询未得到报价的否定结果?

最佳答案

数据库的设计因应用程序的不同而异,而对于一种实现来说有利的事情对于另一种实现来说将是可怕的。您已经确定了一些对您很重要的事情:


数据访问速度(至少不会降低当前性能)
响应应用程序需求/更改的能力
限制查询的复杂性


在无法看到数据库的完整性以及如何使用数据库的情况下,我将遵循以下原则:

尽可能多地使用存储过程和视图

这只是很好的设计。您可以在应用程序和数据表之间创建一个适配器层,这使您可以对数据库(以及视图/存储过程)进行所需的任何更改,而不必更改应用程序本身。对系统进行解耦可以大大简化维护。这也对安全性有好处,好像外部人访问数据的唯一方法是通过存储的procs,您就消除了一些攻击途径。 (关于DBMS是否将缓存存储过程的执行计划,这使它们比类似的查询执行得更快,也存在争议,但我不是DBA或DBDev,因此我不打算讨论。)

尝试限制表格的宽度

我一次又一次地看到的一件事是,每当生产系统中出现需求时,就会在表中添加一列,他们将其称为一天。比重写一堆查询或查看表结构要容易得多。这是可怕的设计。如果您已经按照我的第一条建议限制了对应用程序层的更改,那么就已经限制了以正确的方式实际解决表更改所需的工作。您应该始终评估数据是否属于相关行,或者是否应将其卸载到自己的表中。您不必担心会彻底改变数据库,因为有时这是必要的。

查看您提供的数据,我认为您的第二个选择是可以的。您已经确定了许多实际上代表同一事物的列(“状态更改”或发生的“引用动作”),并将其从主表卸载到辅助表。这是完全可以的,并且可能会有效。您可以通过将状态卸载到其自己的表上并使用整数代替字符串来表示它,从而进一步“欺骗”该表,以使其更快(因为该字符串对数据库无关紧要,并且整数对索引和搜索)。

这并不是说宽表是一件坏事,有时表只需要宽即可。您只需要评估数据是否真正属于数据行所代表的实体。

以新方式处理查询

您将需要使用DBMS的执行计划工具,并了解每个查询的实际工作方式。更改联接顺序可以极大地改变查询的返回速度,因此您不必担心在查询中使用表变量和临时表。它们都是您可使用的工具。

查询否定结果

由于您是专门问这个问题的,所以我会解决。这就需要以一些不同的方式来考虑您的查询(因此,如果您没有这样做,则应该考虑上一门课程或研究《关系代数》教科书,这使理解数据库变得非常容易)。

原始查询使查找报价不被批准的内容变得容易。表格中的所有内容都已包含在内:shared_at为null。简单,轻松自如,没有问题。但是,现在,它位于主表中,而不是位于主表中的列中,它还表示可以执行的所有其他操作。您需要将问题分解一些。

您要查找所有订单中没有表明其已批准的操作的集合。在SQL中看起来像:

 select quote_id from quotes_action where quote_id not in 
           (select quote_id from quotes_action where action = 'approved');


最后的想法

您需要与您的团队坐下来讨论您要如何继续使用该产品。花几天或几周的时间认真思考一下。集思广益....黑客马拉松....做点什么找到想要的解决方案,使您的产品更好,更易于维护。我们所有人都处于一种无法维护的产品的状态,该产品本可以在某个时间点修复,但超出了这一点。尽量不要达到这一点,并在有机会的时候进行修复。

关于mysql - MYSQL DB规范化和查询索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40722532/

相关文章:

mysql - 如何在注册期间加入 PayPal 订阅

mysql - 如何使用 Entity Framework Core 正确运行迁移和播种 docker MySql DB

数据库 "supertable"与更多表与通用表

mysql - 引用表中的重复值

mysql - 从 mysql 中获取行元素日期介于现在和从现在起 5 天之间的行

mysql - 在这种情况下,表上的索引是否有益?

python - 你如何使用 pandas.DataFrame 列作为索引、列和值?

sql - View 包含不精确或不确定的转换?

mysql - 数据规范化——mysql

c# - 选择数据的最佳方式是什么