У меня есть 2 таблицы, а именно:
1. скачанные карточки
2. выкупить билеты
Каждая таблица имеет атрибут «тикетид».
Я хочу найти людей, которые загрузили их билет, но не выкупили их — по сути, найти недублированные (уникальные) тикетиды.
Мой запрос (PHP) выглядит следующим образом:
$sql = "SELECT ticketid
FROM (
SELECT ticketid
FROM downloadedtickets
UNION ALL
SELECT ticketid
FROM redeemedtickets
)
GROUP BY ticketid
HAVING COUNT(*) = 1";
Я не получаю никакого вывода от этого.
Хороший способ сделать это — левое соединение с тестом на NULL.
SELECT d.ticketid
FROM downloadedtickets d
LEFT JOIN redeemedtickets r USING(ticketid)
WHERE r.ticketid IS NULL
Операция LEFT JOIN создает результирующую строку для каждого загруженного билета и его погашения. Там, где нет погашения, redeemed.ticketid имеет значение NULL, поэтому этот запрос использует WHERE для выбора только этих элементов.
Вы должны указать и агрегировать подсчитываемое вами поле. Увидеть ниже:
$sql = "SELECT COUNT(ticketid)ticketid FROM downloadedtickets UNION ALL SELECT ticketid FROM redeemedtickets GROUP BY ticketid HAVING COUNT(ticketid) = 1";
Я бы подошел к этому, используя NOT EXISTS
, NOT IN
, или же LEFT JOIN
, Вот последняя версия:
select d.*
from downloadedtickets d left outer join
redeemedtickets r
on d.ticketid = r.ticketid
where r.ticketid is null;
Ваш запрос, похоже, должен вернуть указанный результат. Одним из возможных объяснений результатов, которые вы получаете (без строк), является то, что нет загруженных билетов, которые не были погашены.
Этот запрос, кажется, предполагает, что ticketid
УНИКАЛЬНО в downloadedtickets
стол, и УНИКАЛЬНЫЙ в redeemedtickets
таблица … это может иметь место, но у нас нет этой информации из предоставленной нами спецификации. (Если это не так, то СЧЕТ (*) для ticketid
от всего downloadedtickets
таблица может быть больше единицы.
Является ticketid
правильный столбец, чтобы использовать, чтобы «соответствовать» строкам из двух таблиц? Мы предполагаем, что это так, потому что это то, что использует ваш запрос. (Если это не так, это также может объяснить результат, который вы получаете.)
Ваш запрос имеет форму (более легко читаемую):
SELECT t.ticketid
FROM ( SELECT d.ticketid
FROM downloadedtickets d
UNION ALL
SELECT r.ticketid
FROM redeemedtickets r
) t
GROUP BY t.ticketid
HAVING COUNT(*) = 1
Мы отмечаем, что существует вероятность того, что этот запрос может вернуть ticketid
для ряда в redeemedtickets
это не в downloadedtickets
, Может быть какая-то гарантия того, что этого не произойдет, но опять же, эта информация отсутствует в спецификации.
Для больших наборов реализация этого встроенного представления может быть дорогой.
Лично я предпочел бы использовать запрос с более эффективным шаблоном «анти-объединения»:
SELECT d.ticketid
FROM downloadedtickets d
LEFT
JOIN redeemedtickets r
ON r.ticketid = d.ticketid
WHERE r.ticketid IS NULL
ORDER BY d.ticketid
Это, по сути, говорит, вернуть все строки из downloadedtickets
вместе с любыми «совпадающими» строками из redeemed
Билеты. LEFT
Ключевое слово делает это «внешним» соединением, поэтому мы получаем все строки из таблицы с левой стороны, независимо от того, есть ли соответствующие строки из таблицы с правой стороны. Хитрость — это предикат в WHERE
предложение, которое отфильтровывает все строки, которые имели совпадение. (Если был матч, мы гарантируем, что ticketid
от redeemedtickets
будет ненулевым. Таким образом, единственными строками, которые будут иметь значение NULL из этой таблицы, будут строки из downloadedtickets
что не было совпадения.
Этот запрос может эффективно использовать индекс redeemedtickets
с ведущей колонной ticketid
,
Это не единственный запрос, который возвращает указанный результат, существуют другие шаблоны запросов, которые могут возвращать эквивалентный результат.