#mysql #subquery #where
#mysql #подзапрос #where-предложение
Вопрос:
SELECT d.userID, (d.amountSuccessfulDeposits), (d.numberSuccessfulDeposits), cl.notes, d.asofdate, cl.send_date
FROM campaign_list cl
INNER JOIN deposit d
on d.userid = cl.userid
WHERE cl.send_date > date(CURDATE()) - INTERVAL 7 DAY
AND cl.send_date < date(CURDATE()) - INTERVAL 1 DAY
AND d.asofdate > date(CURDATE()) - INTERVAL 7 DAY
AND d.asofdate < date(CURDATE()) - INTERVAL 1 DAY
and cl.notes in ('report11', 'report12', 'report13', 'report14', 'report15')
and d.asofdate > cl.send_date
После ввода идентификатора пользователя в кампанию (отчеты 11-15) запрос вычисляет количество внесенных депозитов. «send_date» — это дата ввода идентификатора пользователя в кампанию. Однако, если идентификатор пользователя продвинулся дальше в другой набор кампаний (отчеты 16-20), депозиты, внесенные после ввода 2-го набора кампаний (16-20), не должны учитываться в общем количестве запросов 1-го набора кампаний (11-15).
Пример:
Боб вводит отчет 11 на 1/20
Боб вносит 100 долларов на 1/21
Боб вводит отчет 16 на 1/24
Боб вносит 500 долларов на 1/25
Следовательно, приведенный выше запрос должен возвращать только 100 долларов для Bob.
userID; amountSuccessfulDeposits; numberSuccessfulDeposits; notes; asofdate; send_date
2575192; 100.00; 1; report11 ;2016-01-21 ; 2016-01-20
Следующий запрос не сработал
SELECT d.userID, (d.amountSuccessfulDeposits), (d.numberSuccessfulDeposits), cl.notes, d.asofdate, cl.send_date
FROM campaign_list cl
INNER JOIN deposit d
on d.userid = cl.userid
WHERE cl.send_date > date(CURDATE()) - INTERVAL 7 DAY
AND cl.send_date < date(CURDATE()) - INTERVAL 1 DAY
AND d.asofdate > date(CURDATE()) - INTERVAL 7 DAY
AND d.asofdate < date(CURDATE()) - INTERVAL 1 DAY
and cl.notes in ('report11', 'report12', 'report13', 'report14', 'report15')
and d.asofdate > cl.send_date
AND (
(SELECT (ocl.send_date)
FROM campaign_list ocl
WHERE ocl.userID = cl.userID
and ocl.notes in ('report16', 'report17', 'report18', 'report19', 'report20')
) > d.asofdate
)
Комментарии:
1. Можем ли мы использовать некоторые данные таблицы и желаемый результат? Самый быстрый способ получить качественный ответ здесь.
2. как вы вводите числа в таблицу? когда я копирую и вставляю из клиента mysql, это беспорядок
3. Добавьте четыре пробела перед каждой строкой, что приведет к принудительному моноширинному тексту. Итак, просто создайте красивую таблицу в Блокноте, добавьте четыре пробела в начало каждой строки и вставьте ее в свой вопрос.
4. ОК. Пожалуйста, посмотрите мою редакцию.
Ответ №1:
Я бы использовал встроенное представление, чтобы получить «самую раннюю» дату отправки для последующих кампаний. (Неясно, существует ли какая-либо связь между конкретными кампаниями. Если Боб вводит какую-либо кампанию 16-20, то send_date этой кампании является «отключенным» для всех предыдущих кампаний 11-15)
Без полного понимания спецификации я немного неохотно предлагаю запрос, который будет «работать» для вас.
Но на основе данного запроса я бы сделал что-то вроде этого:
SELECT d.userid
, d.amountSuccessfulDeposits
, d.numberSuccessfulDeposits
, cl.notes
, d.asofdate
, cl.send_date
FROM campaign_list cl
/* Здесь я бы создал встроенное представление, которое получает «самое раннее» send_date
из последующих кампаний 16-20 для каждого идентификатора пользователя. И я делаю внешнее соединение с этим набором, что-то вроде этого */
LEFT
JOIN ( -- inline view to get earliest send_date
SELECT ocl.userid
, MIN(ocl.send_date) AS min_send_date
FROM campaign_list ocl
WHERE ocl.notes IN ('report16'
,'report17'
,'report18'
,'report19'
,'report20'
)
GROUP BY ocl.userid
) oc
ON oc.userid = cl.userid
/* При этом у нас будет «самый ранний» sent_date
из связанной кампании. И теперь это связано с строкой из cl
кампании. Теперь мы можем выполнить соединение с deposit
таблицей. Я предпочитаю помещать все условия для столбцов d
в ON
предложение объединения, а не в WHERE
предложение */
JOIN deposit d
ON d.userid = cl.userid
AND d.asofdate > cl.send_date
AND d.asofdate > DATE(NOW()) INTERVAL -7 DAY
AND d.asofdate < DATE(NOW()) INTERVAL -1 DAY
/* теперь вот в чем фокус… */
AND d.asofdate <= IFNULL(oc.min_send_date,d.asofdate INTERVAL 1 DAY)
включая условие, которое ограничивает строки deposit d
, начиная asofdate
с или после самой ранней send_date
из последующей кампании. Сложная часть заключается в том, что нам нужно условие OR, когда при вводе кампании нет продолжения, самое раннее send_date
будет NULL . Если значение oc.min_send_date
не равно NULL , то мы просто используем это. Если оно равно NULL, мы подставляем значение, которое не будет ограничивать возврат строки из d
. Значение, большее, чем d.asofdate
будет работать.
Я использую <=
(меньший или равный) тест. Если мы просто используем <
тест, похоже, что a deposit
с a asofdate
, который находится в ту же дату, send_date
что и будет ничейной землей… он не будет возвращен для предыдущей кампании, потому что у нас уже есть >
(большее, чем) ограничение d.asofdate > cl.send_date
.
Затем WHERE
предложение с ограничениями на строки из cl
*/
WHERE cl.send_date > DATE(NOW()) INTERVAL -7 DAY
AND cl.send_date < DATE(NOW()) INTERVAL -1 DAY
AND cl.notes IN ('report11'
,'report12'
,'report13'
,'report14'
,'report15'
)
Это подход, который я бы выбрал.
Или ЗАВИСИМЫЙ ПОДЗАПРОС В ПРЕДЛОЖЕНИИ WHERE
Если есть какая-то причина, вам нужно включить зависимый подзапрос в WHERE
предложение… вы могли бы записать его как EXISTS
предикат … проверьте, есть ли какие-либо последующие кампании с send_date раньше, чем asofdate, например
AND NOT EXISTS ( SELECT 1
FROM campaign_list ocl
WHERE ocl.userID = cl.userid
AND ocl.send_date < d.asofdate
AND ocl.notes IN ('report16'
,'report17'
,'report18'
,'report19'
,'report20'
)
)
Или, если есть какая-то причина, по которой вам нужно выполнить скалярное сравнение результатов, полученных из запроса (для сравнения меньше / больше, чем), тогда возврат из подзапроса должен быть скалярным … запрос должен возвращать один столбец и возвращать (не более) одну строку.
Чтобы убедиться, что возвращается не более одной строки, вы можете использовать агрегат (например MIN()
, или вы могли бы использовать LIMIT
предложение. И вы должны быть готовы обработать нулевое значение, которое будет возвращено, если строка не найдена.
AND ( d.asofdate <= IFNULL( ( SELECT ocl.send_date
FROM campaign_list ocl
WHERE ocl.userID = cl.userid
AND ocl.send_date >= d.asofdate
AND ocl.notes IN ('report16'
,'report17'
,'report18'
,'report19'
,'report20'
)
ORDER BY ocl.send_date ASC
LIMIT 1
)
, d.asofdate
)
В MySQL могут быть некоторые ограничения, этот зависимый подзапрос может быть запрещен в WHERE
предложении. Вы могли бы получить это в HAVING
предложении наверняка. Но предложение HAVING оценивается почти последним при выполнении инструкции, после операции GROUP BY .