Laravel Left Join с хитрым фильтром - PullRequest
0 голосов
/ 17 июня 2020

Я назначаю уникальным пользователям ваучеры на своем веб-сайте.1 пользователю может быть назначено более одного ваучера, но ему нельзя назначить один и тот же ваучер дважды. У меня есть 2 mysql таблиц, из которых я извлекаю данные. tbl_users enter image description here tbl_voucher_users enter image description here

Когда пользователь нажимает кнопку на моем веб-сайте, он передает voucher_id , который я использую для отображения соответствующих критериям пользователей, которым может быть назначен этот ваучер (т.е. пользователи, которые не были назначены к этому ваучеру). Ниже показано, как я получаю пользователей, где voucher_id = 8

$user_data = DB::table('users')
        ->leftJoin('voucher_users', 'users.id', '=', 'voucher_users.user_id')
        ->where('voucher_users.voucher_id','!=',8) //User not assigned this voucher
        ->select('users.*','users.id as userID','voucher_users.*')
        ->get();

Моя проблема Я могу покинуть соединение без предложения where и получить результаты как из таблицы Users, так и из таблицы Voucher_users, где удаляет всех пользователей, которым назначен voucher_id = 8 . Однако результаты также включают пользователей, которым назначены другие ваучеры, а также ваучер, который я назначаю. т.е. Ожидаемые результаты: пользователи: 8,11,12,13,14 исключив пользователей: 1,4 Но мои текущие результаты: 4,8,11,12,13,14 Как мне избавиться от пользователя 4, чтобы предотвратить двойное назначение?

Ответы [ 2 ]

0 голосов
/ 17 июня 2020

NOT EXISTS было бы хорошим решением, если бы это оставалось небольшим проектом (или если бы вы могли наложить больше ограничений на пользователей, чтобы сохранить небольшой набор данных, возможно, путем ограничения на основе даты создания пользователя или аналогичного).

Однако, если вы не можете этого сделать, этот запрос в конечном итоге вызовет проблемы, потому что под капотом mysql будет выполнять этот подзапрос для каждой записи, возвращаемой в основном запросе. Вы можете проверить https://dev.mysql.com/doc/refman/5.6/en/subquery-materialization.html для получения дополнительной информации.

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

create temporary table tmp_voucher_user (user_id int not null, primary key (user_id)) as 
select distinct user_id from voucher_users where voucher_id = 8;

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

select users.*, user_voucher.*
from users
inner join user_voucher on users.id = user_voucher.user_id
left join tmp_voucher_user on users.id = tmp_voucher_user.user_id
where tmp_voucher_user.user_id is null -- this part is important, it's only going to grab users where there isn't a match on tmp_user_voucher

К сожалению, это не так чисто, как просто выполнение NOT EXISTS, и я не верю, что Laravel поддерживает способ создания временных таблиц помимо простого написания необработанного запроса, но он должен масштабироваться немного лучше.

0 голосов
/ 17 июня 2020

Благодаря предложению @Kevin Lynch выше использовать НЕ СУЩЕСТВУЕТ. Я упростил код до:

SELECT users.*
FROM
users
WHERE
NOT EXISTS(SELECT user_id FROM voucher_users WHERE voucher_users.user_id = users.id AND voucher_id=9)

Пока он работает, затем я могу скрыть его до Laravel стиля

...