Не могу получить голову MySQL подзапрос - PullRequest
4 голосов
/ 16 августа 2010

У меня возникли проблемы с поиском подзапросов в Mysql.Довольно простые, это нормально, и большинство учебных пособий, которые я нахожу, редко выходят за рамки типичных:

SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);

Я пытаюсь извлечь из своей базы следующее (я постараюсь объяснить это безлюбой фон в нашей базе данных):

Получить список клиентов, принадлежащих к конкретному представителю, и общую сумму, потраченную в прошлом месяце (в одном столбце) и сумму, потраченную в текущем месяце, в другом столбце.

В результате это будет выглядеть примерно следующим образом:

ID | NAME   | PREV MONTH | CUR MONTH
1  | foobar | £2300      | £1200
2  | barfoo | £1240      | £500

Запрос, который я использую для получения первой части данных, выглядит следующим образом:

SELECT c.id,c.name, SUM(co.invoicetotal) as total
FROM customers as c
JOIN customerorders as co on co.customer_id = c.id
WHERE c.salesrep_id = 24
AND co.orderdate BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND CURDATE()
GROUP by c.id
order by total desc

DATE_SUB можно заменитьпо фактическим датам, так как переменные php будут здесь в конце концов.В качестве примера это просто дает мне действительные данные.

Это дает мне, например:

ID | NAME   | TOTAL 
1  | foobar | £2300      
2  | barfoo | £1240   

Таким образом, в идеале, мой подзапрос должен быть точно таким же запросом, но с измененными датами,Я получаю ошибку #1242 - Subquery returns more than 1 row.

Любые предложения или советы, пожалуйста?

Заранее спасибо.Rob

Ответы [ 5 ]

5 голосов
/ 16 августа 2010
SELECT  c.id, c.name,
        (
        SELECT  SUM(co.invoicetotal)
        FROM    customerorders co
        WHERE   co.customer_id = c.id
                AND co.orderdate BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND CURDATE()
        ) AS prev_month,
        (
        SELECT  SUM(co.invoicetotal)
        FROM    customerorders co
        WHERE   co.customer_id = c.id
                AND co.orderdate BETWEEN CURDATE() AND CURDATE() + INTERVAL 1 MONTHS
        ) AS cur_month,
FROM    customers as c
WHERE   c.salesrep_id = 24
ORDER BY
        prev_month DESC
1 голос
/ 16 августа 2010

Я опускаю вычисления даты, поскольку вы генерируете это из кода:

SELECT c.id,c.name, 
    SUM(case when co.orderdate >= @LastMonthStartDate and co.orderdate < @CurrentMonthStartDate then co.invoicetotal else 0 end) as LastMonthTotal,
    SUM(case when co.orderdate between @CurrentMonthStartDate and CURDATE() then co.invoicetotal else 0 end) as CurrentMonthTotalToDate
FROM customers as c 
JOIN customerorders as co on co.customer_id = c.id 
WHERE c.salesrep_id = 24 
   AND co.orderdate BETWEEN @LastMonthStartDate AND CURDATE() --remove this if you want customers that did not order in the last 2 months
GROUP by c.id 
order by total desc 
1 голос
/ 16 августа 2010

Причина ошибки:

WHERE column1 = (SELECT column1 FROM t2)

t2.column1 возвращает более одного результата , но из-за оператора равенства перед подзапросом может быть принято только одно значение. *

Так что вам нужно либо изменить его на IN:

WHERE column1 IN (SELECT column1 FROM t2)

... принять несколько значений. Или измените подзапрос, чтобы он возвращал только одну переменную - в этом примере возвращается наибольшее значение t2.column1 для всей таблицы:

WHERE column1 = (SELECT MAX(column1) FROM t2)

Все зависит от того, какие данные вы пытаетесь получить.

0 голосов
/ 16 августа 2010

Я согласен с JacobM, но предложил немного другой подход:

SELECT
    c.id,
    c.name, 
    SUM(co1.invoicetotal) as PREV_MONTH, 
    SUM(co2.invoicetotal) as CUR_MONTH, 
FROM
    customers as c, 
    customerorders as co1, 
    customerorders as co2
WHERE 
    c.salesrep_id = 24
    and  co1.customer_id = c.id
    and  co2.customer_id = c.id
    AND co1.orderdate BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND CURDATE()
    AND co2.orderdate > DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP by c.id
order by total desc

Не уверен, что будет более эффективным.

0 голосов
/ 16 августа 2010

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

Я предполагаю, что вам нужно создать два подзапроса (один для prev и один для curr) и объединить их по ID пользователя. Примерно так:

SELECT prev.id,prev.name, prev.total, curr.total
FROM
(
SELECT c.id,c.name, SUM(co.invoicetotal) as total  
FROM customers as c JOIN customerorders as co on co.customer_id = c.id  
WHERE c.salesrep_id = 24  
AND co.orderdate BETWEEN DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND CURDATE()  
GROUP by c.id ORDER BY total desc  
) as prev
JOIN
(
SELECT c.id,c.name, SUM(co.invoicetotal) as total  
FROM customers as c JOIN customerorders as co on co.customer_id = c.id  
WHERE c.salesrep_id = 24  
AND co.orderdate > CURDATE()
GROUP by c.id ORDER BY total desc  
) as curr
ON prev.id=curr.id
...