powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Проектирование БД [игнор отключен] [закрыт для гостей] / Посоветуйте алгоритм построения запроса (замена exists(select...))
17 сообщений из 17, страница 1 из 1
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35394659
Alexey Trizno
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Посоветуйте, как бы оптимизировать запросы.

Есть табличка table1 (id, name), и есть подчиненная табличка table2 (id, owner_id, code)
В табличке table2 может быть множество записей для одного owner_id, который ссылается на запись в table1.

Задача, посчитать кол-во записей в table1, для которых существуют в table2 записи с code=5 и code=8 одновременно .

Итоговый запрос такой:

Код: plaintext
1.
2.
3.
4.
5.
6.
select
  count( 1 )
from
  table1 t1
where
  exists(select  1  from table2 where owner_id=t1.id and code= 5 ) and 
  exists(select  1  from table2 where owner_id=t1.id and code= 8 )

Хочется избавиться от exists... но как? Вообще запрос строится динамически, и кол-во этих exists может быть любое.

Помогите мыслью плз.
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35394678
Фотография Ennor Tiegael
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
select count(*)
from table1 t1
where exists (
  select  0  from table2
  where owner_id = t1.id and code in ( 5 ,  8 )
  group by owner_id
  having count(distinct code) >=  2 
  );
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35394707
Alexey Trizno
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ennor Tiegael

беда в том, что условие отбора из table1 может быть любым, не только code=5 and code=8, а вообще любым (юзер пишет), т.е. например (code > 5 and code != 3) or code = 19

более того, если условие например not code=7, то это значит что выбрать записи из table1, для которых нет записи с code=7 в table2. Сейчас это отрабатывается как раз not exists(...)
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35394785
antand
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
select count(*) from table1 t1
where id in (select owner_id from table2 where (code > 5 and code != 3) or code = 19 ......)

А можно вроде как вообще без table1, если с ссылками все в порядке
select count(distinct owner_id) from table2 where (code > 5 and code != 3) or code = 19 ......
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35394790
antand
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ой пардон. Виноват Не до конца в условие задачи прочитал
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35395065
Фотография Ennor Tiegael
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну если условия совсем произвольные, то никак - самый быстрый вариант как раз через (not) exists, и он же единственный более-менее формализуемый.

Интересно, правда, посмотреть, как вы будете реализовывать запрос пользователя вида "code < 7, и чтобы в t2 присутствовали все возможные варианты" .
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35396337
Alexey Trizno
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Запросы строятся по формулам вида:

q6(code=4 or code=5 or code=6) and q5=1 and q1=7 and q999
или
q999 and (q500 or (q11 and not q129))

и т.д. Сами эти формулы абсолютно произвольны и зависят от проектов, и пишутся юзерами.
вот например реальный запрос построеный автоматом по первой формуле:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
select
  count( 1 )
from
  [Respondents] r
  inner join [RespondentCalls] rc on (rc.[ID] = r.[LastRespondentCallID])
where
  (r.[ProjectID] =  647 ) and 
  (exists(select [ID] from [Answers] where ([RespondentCallID] = rc.[ID] and [QuestionNumber] =  6 ) and ([AnswerCode] =  4  or [AnswerCode] =  5  or [AnswerCode] =  6 )) and
   exists(select [ID] from [Answers] where ([RespondentCallID] = rc.[ID] and [QuestionNumber] =  5 ) and ([AnswerCode] =  1 )) and
   exists(select [ID] from [Answers] where ([RespondentCallID] = rc.[ID] and [QuestionNumber] =  1 ) and ([AnswerCode] =  7 )) and
   exists(select [ID] from [Answers] where ([RespondentCallID] = rc.[ID] and [QuestionNumber] =  999 )))

а вот запрос по второй формуле из примера:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
select
  count( 1 )
from
  [Respondents] r
  inner join [RespondentCalls] rc on (rc.[ID] = r.[LastRespondentCallID])
where
  (r.[ProjectID] =  647 ) and 
  (exists(select [ID] from [Answers] where ([RespondentCallID] = rc.[ID] and [QuestionNumber] =  999 )) and
  (exists(select [ID] from [Answers] where ([RespondentCallID] = rc.[ID] and [QuestionNumber] =  500 )) or
  (exists(select [ID] from [Answers] where ([RespondentCallID] = rc.[ID] and [QuestionNumber] =  11 )) and
  not exists(select [ID] from [Answers] where ([RespondentCallID] = rc.[ID] and [QuestionNumber] =  129 )))))

вот такая вот херомантия :)
в принципе, всё конечно работает, но в табличке Answers каждый день становится на 100...500 тысяч записей больше, и постепенно всё замедляется.

в Answers есть уникальный индекс по RespondentCallID + QuestionNumber + AnswerCode, но, почему-то сервер не всегда делает по нему index seek при выполнении exists, т.е. например один exists пользует index scan, а остальные index seek, и на этот scan уходит 27% времени. Никак не удатся заставить _всегда_ пользовать index seek.

беда :(
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35396692
Фотография Ennor Tiegael
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alexey Triznoв Answers есть уникальный индекс по RespondentCallID + QuestionNumber + AnswerCodeНе пробовали его кластерным сделать?
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35396732
Alexey Trizno
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
пробовал, ничего особо не меняется, всё равно сканы проскакивают
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35396743
antand
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я так и подумал что речь идет про анализ чего-то типа звонков и т.п.
Вообще я бы пересмотрел саму бизнес логику такого анализа и соответственно пути решения.
Т.е. делал бы анализ не на основе таких сложных многокритериальных запросов к таблице table2, а к другой таблице(источнику данных) типа промежуточных(сводных) итогов и результатов, которую формировать на основе table2. Или, если задача еще серьезнее, то можно и OLAP задействовать.
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35397511
antand
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я имел ввиду примерно следующее.
Завести группы фильтрации по вашим проектам. Завести таблицы с итогами фильтрации по группам.
Задать и реализовать правила расчета и актуализации данных в таблицах итогов(по расписанию, по триггерам и т.п.)
Выборки для анализа вести уже по таблицам итогов, получая группы и количество групп, в которые входит id из table1. Если речь идет только о вхождении в несколько групп, то можно вообще таблицу итогов с 2 колонками вести типа group_id, owner_id
Пользователь при запросе задает не условия, а список групп, в которые одновременно должен входить
owner_id. Дальше все просто.
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35398611
Alexey Trizno
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Не пройдет такое... это база для хранения результатов телефонных опросов, т.е. здоровенные анкеты. Они постоянно меняются, куча проектов разных со своими анкетами, и эти счетчики должны считаться в реальном времени по реальным данным, иначе смысла вообще не имеет.
И что именно считать - решаю не я :( Сами они придумывают. И главное - все считается по реальным данным, в real time, пока идет сбор данных. И после сбора тоже. По ходу дела делаются выгрузки результатов опять же с такими же фильтрами, на основе тех же формул.
Кол-во вопросов в анкете иногда ужасает.
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35399671
antand
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я понял, интересная задачка.
Но все равно в реальном времени можно считать в промежуточные таблицы итогов, если заранее известны хотя бы часть условий(знают же те, кто составляет анкету, что они хотят получить в итоге. хотя бы начальные цели). Если условие(в вашем случае совокупность условий) по проекту добавляется в реальном времени, то тут же пересчитывать итоги по нему.
Мне кажется, что все равно такой подход будет быстрее, чем каждый раз по пересчитывать заново от всего объема, да еще и сложным неоптимизированным запросом с точки зрения сервера.
Если все так действительно серьезно у Вас с объемами, то на "голом" SQL без применения подобных способов(а лучше вообще средств многомерных баз и анализа и т.п.) нормально решить не получиться.
Можно конечно сервак наращивать до безобразия и т.п., но не предназначен сервер OLTP для решения подобных задач. По сути, если у Вас условия запросов придумываются в реальном времени, то Вы саму многомерную модель анализа строите(меняете) в реальном времени и еще результаты от нее хотите получать в реальном времени.
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35406281
Alexey Trizno
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, я постоянно думаю чего бы такого добавить в структуру, чтобы не проходить по основным данным, но вот что - никак не придумывается. В принципе, при сохранении каждого интервью в базу я могу в памяти по нему просчитать все заданные формулы, и понять, какие формулы сработали, и соответственно куда-то записать результаты. Т.е. например для каждого счетчика (в нем указана формула для расчета значения) хранить список ID звонков, для которых он сработал, и тогда значение счетчика будет равно кол-ву элементов в этом списке. С другой стороны, можно вообще не иметь такого списка, а просто при записи интервью просто увеличивать на 1 значение всех счетчиков, которые затронуты, но, так нельзя, т.к. расчет может фильтроваться по дате звонка и юзеру, который его сделал. Сл-но надо иметь именно табличку вида id, call_id, call_date, user_id, для каждого счетчика.

Правильно размышляю, или опять тупик?
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35406529
_мод
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Можно попробывать закачать промежуточный результат в temp таблицу а потом к ней применить полное условие.
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35407288
Фотография Cat2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
_модМожно попробывать закачать промежуточный результат в temp таблицу а потом к ней применить полное условие.
я бы тоже использовал temp. закачал бы в нее

Код: plaintext
1.
where
  (r.[ProjectID] =  647 ) and   [RespondentCallID] = rc.[ID])

по получившейся небольшой табличке и запрос с кучей exists быстро отработает
...
Рейтинг: 0 / 0
Посоветуйте алгоритм построения запроса (замена exists(select...))
    #35408710
Bely
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alexey Triznoвот такая вот херомантия :)
в принципе, всё конечно работает, но в табличке Answers каждый день становится на 100...500 тысяч записей больше, и постепенно всё замедляется.
Можно попробовать добавлять подзапросы вместо EXIST-ов.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
select
  count( 1 )
from
  [Respondents] r
  inner join [RespondentCalls] rc on (rc.[ID] = r.[LastRespondentCallID])
  left join (select * from [Answers] where ([QuestionNumber] =  999 ) t1 on t1.[RespondentCallID] = rc.[ID] 
  left join (select * from [Answers] where ([QuestionNumber] =  500 ) t2 on t2.[RespondentCallID] = rc.[ID] 
where
  (r.[ProjectID] =  647 ) and 
  ( t1.ID is NOT NULL /* аналог EXISTS */ 
   and t2.ID is NULL /* аналог NOT EXISTS */)

но для этого скорее всего придется переделывать индексы.

Alexey Triznoв Answers есть уникальный индекс по RespondentCallID + QuestionNumber + AnswerCode, но, почему-то сервер не всегда делает по нему index seek при выполнении exists, т.е. например один exists пользует index scan, а остальные index seek, и на этот scan уходит 27% времени. Никак не удатся заставить _всегда_ пользовать index seek.

беда :(что за поле: AnswerCode? в запросах его нету.
Может дело в том, что иногда сервер не осиливает поиск по части индекса (2 поля), если значени есть в трех полях?
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / Проектирование БД [игнор отключен] [закрыт для гостей] / Посоветуйте алгоритм построения запроса (замена exists(select...))
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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