Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Пересечение наборов (списков) на уровне SQL-запроса / 20 сообщений из 20, страница 1 из 1
19.07.2015, 19:54:18
    #39010816
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Есть 2 запроса, каждый из которых возвращает множество идентификаторов (по 3000 штук)
Нужно найти их пересечение.

Просто-напросто эти 2 запроса я вначале объединял через INNER JOIN как положено с использованием индексов. Такой запрос выполняется не очень быстро. Все варианты с разными индексами и разными комбинациями условий испробовал - результат не устраивает.

В то же время каждый из подзапросов отдельно выполняется в 10 раз быстрее. Вот и хочу последовательно их выполнить и найти пересечение. Можно это сделать и на уровне php, но на уровне MySQL должно быть быстрее (т.к. нет лишней передачи данных между сервером и клиентом + выполняется скомпилированный MySQL-код - быстрее интерпретируемого php)

Попробовал так:
Код: sql
1.
2.
SELECT QUERY1 INNER JOIN (QUERY2) AS query2 ON (query1.id = query2.id)
WHERE COND1


Но такой вариант выполняется медленнее, чем первоначальный (т.к. индексов в этом запросе нет, то перебираются каждый с каждым).

Т.е. здесь нужно получать НЕ декартово произведение, а выполнить элементарную арифметическую операцию со списками (пересечение списков).
...
Рейтинг: 0 / 0
19.07.2015, 21:56:31
    #39010849
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Если сделать так:
Код: sql
1.
SELECT query1.id WHERE query1.id IN (QUERY2)

то MySQL начинает выполнять QUERY2 для каждой записи из QUERY1

MySQL настолько туп, что не может определить, что запрос QUERY2 никак не связан с внешним запросом QUERY1 ?
...
Рейтинг: 0 / 0
19.07.2015, 22:15:02
    #39010853
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Т.е. мне вообще непонятно, как в MySQL:
а) ОДНОКРАТНО выполнить подзапрос (который никак не связан с основным запросом)
б) проверить вхождение идентификатора записи из основного запроса в множество идентификаторов, возвращённых вложенным подзапросом
в) при этом скорость выполнения такого запроса должна быть приблизительно такой же (разве что чуть-чуть больше), что и отдельное последовательное выполнение этих запросов
...
Рейтинг: 0 / 0
20.07.2015, 07:22:53
    #39010914
tanglir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Cyrax_02MySQL настолько туп, что не может определить, что запрос QUERY2 никак не связан с внешним запросом QUERY1 ?да
в 5.6 вроде поправили, но насколько качественно, хз

Cyrax_02как
Код: sql
1.
2.
3.
4.
5.
select *
from абырвалг
join (
 подзапрос, который никак не связан с основным запросом
) t0 on абырвалг.id=t0.id

если не устраивает, давайте конкретный текст вашего "каменного цветка"
...
Рейтинг: 0 / 0
20.07.2015, 07:50:45
    #39010921
Merdoc
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Cyrax_02Т.е. мне вообще непонятно, как в MySQL:
в) при этом скорость выполнения такого запроса должна быть приблизительно такой же (разве что чуть-чуть больше), что и отдельное последовательное выполнение этих запросов
Мне кажется это вообще не реально . Сама выборка гораздо менее затратная штука, чем последующая связка.
...
Рейтинг: 0 / 0
20.07.2015, 11:58:09
    #39011156
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
tanglir
Код: sql
1.
2.
3.
4.
5.
select *
from абырвалг
join (
 подзапрос, который никак не связан с основным запросом
) t0 on абырвалг.id=t0.id


Этот вариант я уже прокомментировал:
авторПопробовал так:
Код: sql
1.
2.
SELECT QUERY1 INNER JOIN (QUERY2) AS query2 ON (query1.id = query2.id)
WHERE COND1

Но такой вариант выполняется медленнее, чем первоначальный (т.к. индексов в этом запросе нет, то перебираются каждый с каждым).
...
Рейтинг: 0 / 0
20.07.2015, 12:00:51
    #39011159
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
авторМне кажется это вообще не реально. Сама выборка гораздо менее затратная штука, чем последующая связка.
Как мы сами убедились, на уровне декартовых множеств связка выполняется слишком медленно (очевидно, это связано с тем, что работа идёт со строками, а не с числами + обусловлено декартовой логикой).

Посему нужно вычислять не декартово произведение, а арифметическое. Арифметическое произведение числовых списков.
Либо заставить MySQL выполнять независимый вложенный запрос в WHERE только 1 раз
...
Рейтинг: 0 / 0
20.07.2015, 12:48:44
    #39011232
tanglir
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Cyrax_02Этот вариант я уже прокомментировал:
авторПопробовал так:
Код: sql
1.
2.
SELECT QUERY1 INNER JOIN (QUERY2) AS query2 ON (query1.id = query2.id)
WHERE COND1


Но такой вариант выполняется медленнее, чем первоначальный (т.к. индексов в этом запросе нет, то перебираются каждый с каждым).Прокомментировали, только вот ни реального запроса, ни хотя бы планов не показали.
И почему "WHERE COND1" - снаружи?
...
Рейтинг: 0 / 0
20.07.2015, 13:44:15
    #39011336
biwed.ru
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Cyrax_02, Добрый день.

Если query1.id уникальный, то вашу задачу можно решить через UNION ALL + group by. Почти аналогичная задача приводилась 16945040 , а решение приводил тут 16949405

PS. Было бы здорово, если при постановке задачи приводить таблички с данными, а то писать SQL скрипты как-то не охото.

С уважением,
biwed.ru
...
Рейтинг: 0 / 0
20.07.2015, 14:09:03
    #39011373
bochkov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Cyrax_02Если сделать так:
Код: sql
1.
SELECT query1.id WHERE query1.id IN (QUERY2)

то MySQL начинает выполнять QUERY2 для каждой записи из QUERY1

MySQL настолько туп, что не может определить, что запрос QUERY2 никак не связан с внешним запросом QUERY1 ?
не должен он так делать,
что то тут не так
...
Рейтинг: 0 / 0
20.07.2015, 14:14:39
    #39011381
miksoft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
bochkovCyrax_02Если сделать так:
Код: sql
1.
SELECT query1.id WHERE query1.id IN (QUERY2)

то MySQL начинает выполнять QUERY2 для каждой записи из QUERY1

MySQL настолько туп, что не может определить, что запрос QUERY2 никак не связан с внешним запросом QUERY1 ?не должен он так делать,
что то тут не такТем не менее, такой баг есть. Исправлен он только начиная с версии 5.6.
...
Рейтинг: 0 / 0
20.07.2015, 21:49:47
    #39011744
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
авторне должен он так делать,
что то тут не так Именно так и происходит. Проверил в режиме профилирования

авторЕсли query1.id уникальный, то вашу задачу можно решить через UNION ALL + group byТочно. А ведь сам не догадался... Только в Вашем варианте ещё having нужно будет использовать.
Завтра протестирую этот вариант и сравню со своим (свой вариант я тоже придумал и тоже на UNION). Отпишусь.
...
Рейтинг: 0 / 0
21.07.2015, 12:14:02
    #39012110
alex564657498765453
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
miksoftbochkovпропущено...
не должен он так делать,
что то тут не такТем не менее, такой баг есть. Исправлен он только начиная с версии 5.6.

да, а меня всегда это удивляло...ведь есчё до работы с базой, просто читая книжку по мусклу без привязки к субд - уже знал, что есть кореллированные подзапросы и независимые, и потом очень удивился что мускл их не различает
...
Рейтинг: 0 / 0
21.07.2015, 12:53:36
    #39012170
miksoft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
alex564657498765453miksoftТем не менее, такой баг есть. Исправлен он только начиная с версии 5.6.да, а меня всегда это удивляло...ведь есчё до работы с базой, просто читая книжку по мусклу без привязки к субд - уже знал, что есть кореллированные подзапросы и независимые, и потом очень удивился что мускл их не различаетВообще - различает. Баг касается только конструкции IN (SELECT ... ).
...
Рейтинг: 0 / 0
21.07.2015, 12:59:56
    #39012182
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
авторВообще - различает. Баг касается только конструкции IN (SELECT ... )Если только этой конструкции, тогда её нужно переконструировать так, чтобы логика осталась прежней, но конструкция - иной (в которой глюк не наблюдается).
...
Рейтинг: 0 / 0
21.07.2015, 13:02:08
    #39012185
miksoft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Cyrax_02авторВообще - различает. Баг касается только конструкции IN (SELECT ... )Если только этой конструкции, тогда её нужно переконструировать так, чтобы логика осталась прежней, но конструкция - иной (в которой глюк не наблюдается).Собственно, так всегда и делаем - советуем переписать через JOIN или через EXISTS.
...
Рейтинг: 0 / 0
21.07.2015, 23:06:31
    #39012814
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Результат таков:

1) Классический вариант с INNER JOIN и с использованием наиболее подходящих индексов: 0.08 сек
2) Вариант с IN (SELECT) в версии MySQL до 5.6: 15 сек (подзапрос выполняется много раз - баг MySQL)
3) Вариант с EXISTS (коррелированный подзапрос): 15 сек

4) Мой вариант (получение 2 наборов идентификаторов - на уровне SQL, вычисление пересечения - на уровне php):

Код: sql
1.
2.
3.
4.
SELECT (
    (SELECT GROUP_CONCAT(DISTINCT ... SEPARATOR '_') AS ids FROM ... WHERE ...) AS ids1,
    (SELECT GROUP_CONCAT(DISTINCT ... SEPARATOR '_') AS ids FROM ... WHERE ...) AS ids2
)

Код: php
1.
2.
$idsPacked = ВЫПОЛНЯЕМ ЗАПРОС
$fastIds = array_intersect(explode('_', $idsPacked[0]['ids1']), explode('_', $idsPacked[0]['ids2']));

Здесь запрос выполняется = 0.0238-0.241 сек, [explode + intersect] = 0.0050-0.0052 сек. Итого: 0.0289-0.0294 сек

5) Вариант, предложенный biwed.ru :
Код: sql
1.
2.
3.
4.
5.
6.
SELECT IDS.id FROM (
    SELECT DISTINCT ... AS id, 1 AS intersect
UNION ALL
    SELECT DISTINCT ... AS id, 1 AS intersect
) AS IDS
GROUP BY IDS.id HAVING (SUM(intersect) = 2)

Здесь обязательно:
а) должны присутствовать DISTINCT (чтобы в пределах одной группы идентификаторов нельзя было набрать [intersect = 2])
б) должен быть UNION ALL (чтобы сохранить все идентификаторы, присутствующие в обоих выборках)

Такой запрос выполняется 0.0277-0.283 сек , что на 0.001 сек быстрее предыдущего варианта (вывод достоверен - тестировал много раз).

Причём если во второй выборке не указывать имена полей [AS id] и [AS intersect] (во второй выборке они не обязательны), то запрос будет выполняться на 0.0005 сек медленнее и соответственно, выигрыш по сравнению с предыдущим вариантом составит 0.0005 сек .

------------------------------------------------------------------------------------------------------
6) Вариант с IN (SELECT) в версии MySQL 5.6 и выше: ??? сек
Скорее всего, этот вариант сработает быстрее всех предложенных.
...
Рейтинг: 0 / 0
21.07.2015, 23:11:12
    #39012819
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
авторТакой запрос выполняется 0.0277-0.283 сек
Опечатка. Должно быть так: 0.0277-0. 0 283 сек
...
Рейтинг: 0 / 0
22.07.2015, 03:30:40
    #39012912
biwed.ru
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
Cyrax_02, Добрый день.
Cyrax_02 Результат таков:

5) Вариант, предложенный biwed.ru :
Код: sql
1.
2.
3.
4.
5.
6.
SELECT IDS.id FROM (
    SELECT DISTINCT ... AS id, 1 AS intersect
UNION ALL
    SELECT DISTINCT ... AS id, 1 AS intersect
) AS IDS
GROUP BY IDS.id HAVING (SUM(intersect) = 2)

Здесь обязательно:
а) должны присутствовать DISTINCT (чтобы в пределах одной группы идентификаторов нельзя было набрать [intersect = 2])
б) должен быть UNION ALL (чтобы сохранить все идентификаторы, присутствующие в обоих выборках)

DISTINCT можно убрать переписав запрос. Получится как-то так.
Код: sql
1.
2.
3.
4.
5.
6.
SELECT IDS.id FROM (
    SELECT  ... AS id, 1 AS intersect_1, NULL AS intersect_2
UNION ALL
    SELECT  ... AS id, NULL, 1
) AS IDS
GROUP BY IDS.id HAVING (MIN(intersect_1) = MIN(intersect_2))



С уважением,
biwed.ru
...
Рейтинг: 0 / 0
22.07.2015, 13:44:29
    #39013361
Cyrax_02
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Пересечение наборов (списков) на уровне SQL-запроса
biwed.ruDISTINCT можно убрать, переписав запрос. Получится как-то так:
Код: sql
1.
2.
3.
4.
5.
6.
SELECT IDS.id FROM (
    SELECT  ... AS id, 1 AS intersect_1, NULL AS intersect_2
UNION ALL
    SELECT  ... AS id, NULL, 1
) AS IDS
GROUP BY IDS.id HAVING (MIN(intersect_1) = MIN(intersect_2))

Данная модификация (без DISTINCT) ускоряет запрос ещё на 0.0015 сек .
...
Рейтинг: 0 / 0
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Пересечение наборов (списков) на уровне SQL-запроса / 20 сообщений из 20, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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