向数据库添加索引会更改查询结果

时间:2019-04-12 08:35:04

标签: sql mariadb

我正在尝试为简单查询添加索引,但是添加索引会更改结果。索引是否可能影响查询结果?

当我删除索引时,结果返回到以前的状态。

查询是如此简单:

SELECT `gid`.`num_version_contrat` AS `num_version_contrat`, MAX(`gid`.`date_quittancement_echeance`) AS `max_date_quittancement_echeance`,`gid`.`montant_ht_actualise_echeance` AS `dernier_montant`
FROM `gid`
WHERE `gid`.num_version_contrat = "100313 V.0"
GROUP BY `gid`.`num_version_contrat`
ORDER BY `gid`.`num_version_contrat`

没有索引的结果是:

  

“ num_version_contrat”,“ max_date_quittancement_echeance”,“ dernier_montant”:

     

“ 100313 V.0”,“ 2018-04-01”,“ 32744”

添加索引:

CREATE INDEX `gid_idx_group_by_index` ON `gid` (`num_version_contrat`, `date_quittancement_echeance`, `montant_ht_actualise_echeance`)

具有索引的结果:

  

“ num_version_contrat”,“ max_date_quittancement_echeance”,“ dernier_montant”:

     

“ 100313 V.0”,“ 2018-04-01”,“ 2067.64”

您了解为什么两种情况下的结果都不同吗?

1 个答案:

答案 0 :(得分:5)

您在select子句中有一个不在以下项分组的字段:gidmontant_ht_actualise_echeance

这是MySQL和MariaDB的非常危险的功能,可能会导致意外的结果。

其他数据库将拒绝您的查询,但是除非您的SQL模式包含“ ONLY_FULL_GROUP_BY”,MariaDB会接受该查询,然后为您提供读取时遇到的第一个值。

添加索引会更改检索订单记录的方式,因此您会有所不同。实际上,即使添加/更新/删除其他记录也可能会更改组的结果,因为它可能会更改记录所在的块。

您可以通过将gidmontant_ht_actualise_echeance添加到Group by语句来修正查询。

或者,您可以选择一个聚集函数来计算总和,最大值,first_value或last_value。

回复评论:

GROUP BY的意思是“对于这些字段的每一个组合,都做一个记录”。因此,如果您具有“按年,月分组”,则您将在表中找到每年和每月组合的一条记录。 此外,将所有您知道在组中具有唯一值的值都放在此处。这意味着“季度”应该放在这里,因为一个月始终具有唯一的季度价值。如果所有记录的值都只有一个,则“公司名称”也应该放在此处。

对于所有其他字段,您需要告诉数据库如何处理找到的多个值。数字字段很简单:您可以求和(价格)或COUNT(id)等。对于文本字段,您需要选择:MIN,MAX(按字母顺序排列),FIRST_VALUE(即现在隐含的值)或什至GROUP_CONCAT将所有值附加到一个字符串中。

要获取与最后{max)giddate_quittancement_echeance关联的gid.montant_ht_actualise_echeance的值,您需要首先标识具有最大日期的记录,并使用这些记录来选择所需的值从桌子上。

在MySQL / MariaDB中,通常是通过自联接表来完成的。如果表中的键/ ID列不明确,请使用该列进行联接,但如果没有,则将是这样:

SELECT `gid`.`num_version_contrat` AS `num_version_contrat`,
`gid`.`date_quittancement_echeance` AS `max_date_quittancement_echeance`, 
`gid`.`montant_ht_actualise_echeance` AS `dernier_montant`
FROM `gid`
INNER JOIN
(
    SELECT `gid`.`num_version_contrat` AS `num_version_contrat`, 
    MAX(`gid`.`date_quittancement_echeance`) AS max_date
    FROM `gid`
    GROUP BY `gid`.`num_version_contrat`
) last_dates
ON `gid`.`num_version_contrat` = `last_dates`.`num_version_contrat`
AND `gid`.`date_quittancement_echeance` = `last_dates`.`max_date`
WHERE `gid`.num_version_contrat = "100313 V.0"

ORDER BY `gid`.`num_version_contrat`

第一部分选择所需的字段。第二部分仅查找每个合同版本的max_dates,而INNER JOIN仅保留两个合同中找到的记录,并删除所有没有max_date的记录。

这假定WHERE子句仅用于测试,以后将被删除。 否则,整个分组依据就没有意义。