简体   繁体   中英

Group By not functioning as desired

Let's say I have three tables - Orders, OrderDetails, and ProductType - and the Orders table includes a column for Customer. What I need to do is write a query that will show me a list of customers and how many orders each customer has placed, as well as displaying and grouping by another column, which is a boolean based on whether a particular type of product - say, telephones - is in the order.

For example, we might have:

Customer | NumOrders | IncludesPhone
---------------------------------
Jameson | 3 | Yes
Smith | 5 | Yes
Weber | 1 | Yes
Adams | 2 | | No
Jameson | 1 | No
Smith | 7 | No
Weber | 2 | No

However, when I try to write the query for this, I'm getting multiple rows with the same values for Customer and IncludesPhone, each with a different value for NumOrders. Why is this happening? My query is below:

SELECT Customer, COUNT(Customer) AS NumOrders, CASE WHEN (ProductType.Type = 'Phone') THEN 'Yes' ELSE 'No' END AS IncludesPhone
FROM Orders INNER JOIN OrderDetails INNER JOIN ProductType
GROUP BY Customer, Type
Order By IncludesPhone, Customer

Change the group by to

GROUP BY    Customer, 
            CASE WHEN (ProductType.Type = 'Phone') THEN 'Yes' ELSE 'No' END

This query should work

SELECT Customer, COUNT(Customer) AS NumOrders, 
CASE WHEN (ProductType.Type = 'Phone') THEN 'Yes' ELSE 'No' END AS IncludesPhone
FROM Orders INNER JOIN OrderDetails INNER JOIN ProductType
GROUP BY    Customer, 
CASE WHEN (ProductType.Type = 'Phone') THEN 'Yes' ELSE 'No' END
Order By IncludesPhone, Customer

Since you're grouping on both Customer and Type , your query returns the count of orders per customer per type. If you only want one row per customer, you should only group by Customer , and then use something like this, to determine whether a given customer bought a phone:

SELECT Customer, COUNT(Customer) AS NumOrders, 
    CASE 
        WHEN SUM(CASE WHEN (ProductType.Type = 'Phone') THEN 1 ELSE 0 END) > 0 
        THEN 'Yes' 
    ELSE 'No' END AS IncludesPhone
FROM Orders INNER JOIN OrderDetails INNER JOIN ProductType
GROUP BY Customer
Order By IncludesPhone, Customer

The inner sum basically counts the number of phones bought per customer. If this is more than 0, then the customer bought at least one phone and we return "Yes".

That's because you're grouping by Type column, so there could be duplicate rows. For example, for types 'Email' and 'Personal' column IncludesPhone will be 'No', but as you're grouping by Type there would be two records in the output.

To fix this, you can use same expression in the group by clause or use subquery or Common Table Expression :

with cte as (
    select
        Customer,
        case when pt.Type = 'Phone' then 'Yes' else 'No' end as IncludesPhone
    from Orders as o
        inner join OrderDetails as od -- ???
        inner join ProductType as pt -- ???
)
select Customer, IncludesPhone, count(*) as NumOrders
from cte
group by Customer, IncludesPhone
order by IncludesPhone, Customer

using same expression in the group by clause:

select
    Customer,
    case when pt.Type = 'Phone' then 'Yes' else 'No' end as IncludesPhone,
    count(*) as NumOrders
from Orders as o
    inner join OrderDetails as od -- ???
    inner join ProductType as pt -- ???
group by Customer, case when pt.Type = 'Phone' then 'Yes' else 'No' end

You could try this query:

SELECT  x.Customer,x.NumOrders,
        CASE WHEN x.NumOrders>0 AND EXISTS(
            SELECT  *
            FROM    Orders o
            INNER JOIN OrderDetails od ON ...
            INNER JOIN ProductType pt ON ...
            WHERE   o.Customer=x.Customer
            AND pt.Type = 'Phone'
        ) THEN 1 ELSE 0 END IncludesPhone
FROM
(
    SELECT  Customer,COUNT(Customer) AS NumOrders
    FROM    Orders 
    GROUP BY Customer
) x
Order By IncludesPhone, x.Customer;

or this one:

SELECT  o.Customer, 
        COUNT(o.Customer) AS NumOrders, 
        MAX(CASE WHEN EXISTS
        (
            SELECT  *
            FROM    OrderDetails od
            JOIN    ProductType pt ON ...
            WHERE   o.OrderID=od.OrderID -- Join predicated between Orders and OrderDetails table
            AND     ProductType.Type = 'Phone'
        ) THEN 1 ELSE 0 END) AS IncludesPhone
FROM    Orders o
GROUP BY Customer
ORDER BY IncludesPhone, o.Customer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM