powered by simpleCommunicator - 2.0.49     © 2025 Programmizd 02
Форумы / MySQL [игнор отключен] [закрыт для гостей] / За типами данных надо следить!
12 сообщений из 12, страница 1 из 1
За типами данных надо следить!
    #40121581
Фотография Akina
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
"Мягкая" система типов в MySQL, с неявным приведением по контексту - расслабляет. Сегодня очередной раз убедился, что за типами данных надо следить. Суть того, что получилось сегодня, продемонстрирую на модели.

Создадим модельную таблицу:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
CREATE TABLE test (id INT AUTO_INCREMENT PRIMARY KEY, dt DATE, val INT);

SET @@cte_max_recursion_depth := 1000000;

INSERT INTO test
WITH RECURSIVE
cte AS ( SELECT 1 n
         UNION ALL
         SELECT n + 1 FROM cte WHERE n < 1000000 )
SELECT NULL, '2021-01-01' + INTERVAL RAND()*365 DAY, RAND()*1000
FROM cte;


Миллион записей, даты в пределах текущего года, в каждой записи - некое значение.

Задача - для каждой даты подсчитать сумму этих неких значений. Предполагая, что не все даты имеются в таблице (генерация данных делает такую ситуацию почти нереальной, но предположим...), использовать генерируемую таблицу-календарь.

То есть вроде бы запрос очевиден:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
WITH RECURSIVE
cte AS ( SELECT '2021-01-01' dt
         UNION ALL
	 SELECT dt + INTERVAL 1 DAY FROM cte WHERE dt < '2022-01-01' )
SELECT cte.dt, SUM(test.val) sumval
FROM cte
LEFT JOIN test USING (dt)
GROUP BY cte.dt;


На моей рабстанции этот модельный запрос выполняется где-то за 3,27-3,31 секунды.

Конечно, запрос хочется немного ускорить. Создадим покрывающий индекс:
Код: sql
1.
CREATE INDEX idx ON test (dt, val);



Отлично. Запрос выполняется за 2,88-2,92 секунды.

Можно ли его ещё ускорить?

Оказывается, можно, да ещё как! и всего-то для этого надо обратить внимание на типы.

Поле dt в таблице, по которому выполняется связывание, имеет тип DATE. А что насчёт рекурсивного CTE? А вот там тип получающегося поля определяется якорем (нерекурсивным запросом), и оно - строковое!

Изменим генератор календаря так, чтобы он сразу генерировал поле типа DATA:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
WITH RECURSIVE
cte AS ( SELECT DATE '2021-01-01' dt
         UNION ALL
	 SELECT dt + INTERVAL 1 DAY FROM cte WHERE dt < '2022-01-01' )
SELECT cte.dt, SUM(test.val) sumval
FROM cte
LEFT JOIN test USING (dt)
GROUP BY cte.dt;


Есть! время выполнения запроса уменьшилось вдвое, и теперь составляет 1,41-1,45 секунды.

Если не создавать индекса, разница не столь значительна - начальные 3,27-3,31 секунды уменьшаются до 2,51-2,55 секунды. Но всё равно - вполне заметно.

В общем, не надейтесь на MySQL, следите за типами сами. И вам воздастся.
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40121810
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А если так?
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
WITH RECURSIVE
cte AS ( SELECT DATE '2021-01-01' dt
         UNION ALL
	 SELECT dt + INTERVAL 1 DAY FROM cte WHERE dt < '2022-01-01' ),
agregat AS (SELECT test.dt, SUM(test.val) FROM test)
SELECT cte.dt, SUM(test.val) sumval
FROM cte
LEFT JOIN agregat AS test USING (dt)
GROUP BY cte.dt;

В синтаксисе мог наврать, но идея, надеюсь, понятна.
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40121833
Фотография Akina
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miksoft
А если так?

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
WITH RECURSIVE
cte AS ( SELECT DATE '2021-01-01' dt
         UNION ALL
	 SELECT dt + INTERVAL 1 DAY FROM cte WHERE dt < '2022-01-01' ),
agregat AS (SELECT dt, SUM(val) sumval FROM test GROUP BY dt)
SELECT cte.dt, sumval
FROM cte
LEFT JOIN agregat AS test USING (dt)
GROUP BY cte.dt;


В общем, конечно, так ещё лучше.

Без индекса и DATE - 1,61
Без индекса с DATE - 1,44
С индексом без DATE - 0,83
С индексом и DATE - 0,56

Разница от явного приведения типа - всё равно значительна.
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122041
Фотография Alex_Ustinov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Akina,

очень полезно. CAST часто необходим оказывается.
с agregat не понял но тоже + скорость. надо почитать
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122043
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Akina
Без индекса и DATE - 1,61
Без индекса с DATE - 1,44
С индексом без DATE - 0,83
С индексом и DATE - 0,56

Разница от явного приведения типа - всё равно значительна.
Что немного странно для столь малого набора данных - всего лишь сотни записей.
Код: sql
1.
2.
3.
SELECT DATE '2021-01-01' dt
         UNION ALL
	 SELECT dt + INTERVAL 1 DAY FROM cte WHERE dt < '2022-01-01'

А этот фрагмент сильно зависит от указания DATE ?
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122044
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Akina
Код: sql
1.
dt < '2022-01-01'

Кстати, сюда, наверное, тоже имеет смысл DATE вставить.
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122046
Фотография Alex_Ustinov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miksoft
Akina
Код: sql
1.
dt < '2022-01-01'

Кстати, сюда, наверное, тоже имеет смысл DATE вставить.
нет, не влияет на данном примере, чистый скаляр
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122047
Фотография Alex_Ustinov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ну и если кто-то хоть чуть-чуть заглядывал в С/С++ то там как правило указывать тип данных
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122059
Фотография Akina
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miksoft
Что немного странно для столь малого набора данных - всего лишь сотни записей.
Агрегация миллиона записей в три с половиной сотни? да нормально, у меня ж не сервер на три десятка голов.

miksoft
А этот фрагмент сильно зависит от указания DATE ?

Да собственно именно тут и нужно указание типа, именно этот DATE и убирался, что приводило к увеличению времени обработки.

miksoft
Кстати, сюда, наверное, тоже имеет смысл DATE вставить.

нет, если в сравнении один из операндов имеет тип даты-времени, а второй константа, то второй приводится к этому типу. https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed.
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122329
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Akina
miksoft
А этот фрагмент сильно зависит от указания DATE ?

Да собственно именно тут и нужно указание типа, именно этот DATE и убирался, что приводило к увеличению времени обработки.
Я имел в виду, если его самостоятельно запустить. Без остальной части запроса.
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122330
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Akina
miksoft
Что немного странно для столь малого набора данных - всего лишь сотни записей.
Агрегация миллиона записей в три с половиной сотни?
Речь была про это:
Akina
miksoft
А если так?

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
WITH RECURSIVE
cte AS ( SELECT DATE '2021-01-01' dt
         UNION ALL
	 SELECT dt + INTERVAL 1 DAY FROM cte WHERE dt < '2022-01-01' ),
agregat AS (SELECT dt, SUM(val) sumval FROM test GROUP BY dt)
SELECT cte.dt, sumval
FROM cte
LEFT JOIN agregat AS test USING (dt)
GROUP BY cte.dt;



В общем, конечно, так ещё лучше.

Без индекса и DATE - 1,61
Без индекса с DATE - 1,44
С индексом без DATE - 0,83
С индексом и DATE - 0,56

Разница от явного приведения типа - всё равно значительна.
Указание DATE снижает время запроса на 0,2-0,3 секунды, а влияет всего лишь на несколько сотен конвертаций.
Агрегацию я специально предложил убрать в подзапрос, чтобы join с конвертацией был не на миллион записей, а на сотни.
...
Рейтинг: 0 / 0
За типами данных надо следить!
    #40122341
Фотография Akina
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miksoft
а влияет всего лишь на несколько сотен конвертаций.
Нет, основное влияние - это необходимость конвертации в условии связывания основного запроса. А её необходимость зависит от того, какой тип данных отдаёт CTE.

miksoft
Я имел в виду, если его самостоятельно запустить. Без остальной части запроса.

Тут как раз разницы нет - вернее, есть, но она не регистрируется, слишком мала.
...
Рейтинг: 0 / 0
12 сообщений из 12, страница 1 из 1
Форумы / MySQL [игнор отключен] [закрыт для гостей] / За типами данных надо следить!
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]