MYSQL数据库规范化&;查询索引

MYSQL数据库规范化&;查询索引,mysql,indexing,database-normalization,Mysql,Indexing,Database Normalization,我们目前有一个包含90列的表,随着表的增长和业务需求的变化,我们不得不大量修改表(添加/删除cols和索引) 对于该应用程序,我们的工作人员不断查询上述数据的各种变化,例如已完成(已完成)、已检查(管理员检查)和未删除、已审核(已删除、已审核) 我们认为将其中一些列卸载到它们自己的行中可能更容易,我们将其称为quotes\u actions,然后在查询时执行一些连接 |------ (Table name: quotes_actions) |Column|Type|Null|Default |

我们目前有一个包含90列的表,随着表的增长和业务需求的变化,我们不得不大量修改表(添加/删除cols和索引)

对于该应用程序,我们的工作人员不断查询上述数据的各种变化,例如已完成(已完成)、已检查(管理员检查)和未删除、已审核(已删除、已审核)

我们认为将其中一些列卸载到它们自己的行中可能更容易,我们将其称为quotes\u 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

我们已经将150000行的数据拆分成这种格式,它既不比使用正确的索引查询原始数据库快也不比查询慢

有没有人对此有经验,对每种方法都有什么建议或缺陷?添加覆盖索引和按需要向原始表中添加列需要花费大量时间,而第二种方法将索引设置就绪,但会引入更多的联接和更复杂的查询

0.09s
select * from `quotes` 
where `completed_at` is not null 
and `approved_at` is not null 
and deleted_at is null
=>


此外,如果第二种方法更好,如果报价未被批准,如何查询负面结果

数据库设计因应用程序而异,对于一个实现来说很好的东西对于另一个实现来说很糟糕。您已经确定了一些对您很重要的事情:

  • 数据访问速度(至少不会降低当前性能)
  • 能够响应应用程序需求/更改
  • 限制查询的复杂性
在看不到数据库的完整性以及如何使用数据库的情况下,我将遵循以下原则:

尽可能多地使用存储过程和视图 这是一个很好的设计。您可以在应用程序和数据表之间创建一个适配器层,它允许您在数据库(以及视图/存储过程)中进行任何需要的更改,而无需更改应用程序本身。将系统解耦可以大大简化维护。此外,这对安全性也有好处,因为如果外部人员访问数据的唯一方式是通过存储的进程,那么您已经消除了一些攻击途径。(关于DBMS是否会缓存存储过程的执行计划,使它们比类似的查询执行得更快,也有争论,但我不是DBA或DBDev,所以我不涉及这一点)

尝试限制表的宽度 我一次又一次看到的一件事是,每当生产系统中出现需求时,一列被添加到表中,他们称之为一天。比重写一堆查询或检查表结构容易得多。这是个糟糕的设计。如果您已经按照我的第一条建议限制了应用层所需的更改,那么您已经限制了以正确方式实际解决表更改所需的工作。您应该始终评估数据是否属于所讨论的行,或者是否应该将其卸载到自己的表中。您不应该害怕从根本上改变您的数据库,因为有时这是必要的

看看你提供的数据,我认为你的第二个选择是可以的。您已经确定了许多实际表示相同内容的列(“状态更改”或您所说的发生的“报价操作”),并将其从主表卸载到辅助表。这是非常好的,而且可能会有效。通过将状态转移到自己的表中,并使用整数而不是字符串来表示状态,您可以进一步“欺骗”以加快此表的速度(因为字符串对数据库不重要,而整数的索引和搜索速度要快得多)

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

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

查询否定结果 既然你特别问了这个问题,我就来回答。这需要以一种稍微不同的方式来思考您的查询(因此,如果您没有,那么您应该学习一门课程或阅读一本关系代数教科书,这使理解数据库变得更加容易)

您最初的查询使得查找报价不被批准的地方变得很容易。这一切都在表中:approved_at为空。简单,容易,没有问题。然而,现在,它不是在主表的一列中,而是在它自己的表中,它还表示可以采取的所有其他操作。你需要把这个问题分解一下

您希望找到一个集合,其中在所有订单中,没有表示其已批准的操作。在SQL中,如下所示:

 select quote_id from quotes_action where quote_id not in 
           (select quote_id from quotes_action where action = 'approved');
最后的想法
您需要与您的团队坐下来讨论您希望如何推进此产品。花几天或几周的时间认真思考。集思广益……黑客大会……做点什么来找到你喜欢的解决方案,让你的产品更好、更易维护。我们都曾遇到过这样的情况:我们有一个无法维护的产品,该产品本可以在某个时候修复,但却超出了这一点。尽量不要达到这一点,并在有机会的时候修复它。

数据库设计因应用程序而异,对一个实现来说很好的东西对另一个实现来说很糟糕。您已经确定了一些对您很重要的事情:

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
 select quote_id from quotes_action where quote_id not in 
           (select quote_id from quotes_action where action = 'approved');