Выберите все, что имеет точные поля - PullRequest
0 голосов
/ 18 сентября 2018

У меня есть три таблицы: потоки , типы_документов и документы . Потоки имеют много документов, которые относятся к типу документа.

Допустим, мне нужно выбрать все потоки, в которых есть документы, принадлежащие к определенному списку типов документов, например, где идентификатор типа документа в 1, 2, 3 и 4. Другими словами, я хочу выбрать только потоки у которого есть документы со ВСЕМ вышеупомянутым идентификатором типа документа. Как использовать логику / запрос?

Моя первая попытка была с where in, но она не гарантирует, что документы имеют все типы документов, она запрашивает хотя бы один:

select * from flows where id in (
    select flow_id from documents where document_type_id in (1, 2, 3, 4)
);

Я должен написать свои запросы с помощью Laravel Eloquent, но после обнаружения правильной логики это будет тривиально.

Ответы [ 3 ]

0 голосов
/ 18 сентября 2018

Вы можете использовать агрегацию и having:

select f.*
from flows f
where f.id in (select d.flow_id
               from documents d
               where d.document_type_id in (1, 2, 3, 4)
               group by d.flow_id
               having count(distinct d.document_type) = 4
              );

. = 4 гарантирует, что все четыре типа найдены в documents.Обратите внимание, что я также добавил псевдонимы таблиц и уточнил все ссылки на столбцы.Это хорошие идеи для любых запросов, которые вы пишете.

Вы также можете сделать это с помощью коррелированного подзапроса, который может быть более эффективным в MySQL:

select f.*
from flows f
where exists (select 1
              from documents d
              where d.document_type_id in (1, 2, 3, 4) and
                    d.flow_id = f.id
              having count(distinct d.document_type) = 4
             );

В частности, этим можно воспользоватьсяиндекса на documents(flow_id, document_type).

0 голосов
/ 18 сентября 2018

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

$flows = Flow::has('documents', '=', 4)->get();

Это будет зависеть от правильной настройки ваших отношений и, возможно, от уникального ключа в вашей базе данных, который не позволитпоток, чтобы в вашу сводную таблицу было добавлено более 1 документа.

Также было бы разумно сделать эти 4 динамическими, посчитав сначала количество документов и отбросив эту переменную подсчета в запрос, а не 4.

https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.html#method_has

Более персонализированное решение ...

$documentIds = [1,2,3,4];

$flows = Flow::whereHas('documents', function($q) use ($documentIds) {
    $q->whereIn('document_type_id', $documentIds);
}, '=', count($documentIds));
0 голосов
/ 18 сентября 2018

По сути, используя GROUP_CONCAT , вы можете получить DISTINCT document_type_id (s) для идентификатора потока , объединенного в строку через запятую.Используйте предложение HAVING для последующей фильтрации.

Вот пример запроса (Пожалуйста, измените имена таблиц и столбцов соответственно):

SELECT f.*, 
       GROUP_CONCAT(DISTINCT d.document_type_id 
                    ORDER BY d.document_type_id ASC) AS document_types_in_flow 
FROM flows AS f 
INNER JOIN documents AS d ON d.flow_id = f.id 
GROUP BY f.id 
HAVING document_types_in_flow = '1,2,3,4' 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...