Добавление индекса в базу данных изменяет результаты запроса - PullRequest
1 голос
/ 12 апреля 2019

Я пытаюсь добавить индекс для простого запроса, однако добавление его приводит к изменению результатов. Возможно ли, что индексы влияют на результаты запроса?

Когда я удаляю обратно индекс, результаты возвращаются такими, какими они были раньше.

Запрос так же прост:

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 Ответ

5 голосов
/ 12 апреля 2019

В предложении выбора есть поле, которого нет в группе: gid. montant_ht_actualise_echeance

Это очень опасная функция MySQL и MariaDB, которая может дать неожиданные результаты, как вы выяснили.

Другие базы данных отклонят ваш запрос, но, если ваш режим SQL не содержит «ONLY_FULL_GROUP_BY», MariaDB примет запрос и затем предоставит вам первое значение, с которым он столкнется при чтении.

Добавление индекса изменяет порядок получения записей заказов, поэтому вы получаете что-то другое. Фактически, даже добавление / обновление / удаление других записей может изменить результат группы, поскольку может измениться, в каком блоке находится запись.

Вы можете исправить свой запрос, добавив gid. montant_ht_actualise_echeance в оператор Group by.

В качестве альтернативы вы можете выбрать агрегированную функцию для вычисления суммы, max, first_value или last_value.

В ответ на комментарий:

GROUP BY означает «Для каждой комбинации этих полей сделайте одну запись». Поэтому, если у вас есть «GROUP BY year, month», вы получите одну запись для каждой комбинации года и месяца, которая находится в таблице. Кроме того, вы помещаете здесь все значения, которые, как вы знаете, имеют уникальное значение внутри групп. Это означает, что «четверть» должна идти сюда, поскольку месяц всегда имеет уникальное значение для квартала. «Название компании» также должно указываться там, если для всех записей есть только одно значение.

Для всех других полей вам нужно указать базе данных, как обрабатывать найденные множественные значения. Числовые поля просты: вы можете SUM (цена) или COUNT (идентификатор) и т. Д. Для текстовых полей вам нужно выбрать: MIN, MAX (в алфавитном порядке), FIRST_VALUE (это то, что у вас есть сейчас, неявно) или даже GROUP_CONCAT для добавления всех значений в одну строку.

Чтобы получить значение gid.montant_ht_actualise_echeance, связанного с последним (макс.) gid. date_quittancement_echeance, вам необходимо сначала определить записи с максимальной датой и использовать их для выбора нужных значений из таблицы.

В MySQL / MariaDB это чаще всего делается путем самостоятельного присоединения к таблице. Если в таблице есть столбец unqiue key / 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 предназначено только для тестирования и будет удалено позже. В противном случае вся группа не имеет смысла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...