求高效SQL查询:获取付清全部订单或未下单的客户姓名
Alright, let's tackle this problem to get the exact customer groups you need—those who've paid for every single order they placed, plus those who never placed any orders at all. Below are two efficient SQL query approaches, tailored to the one-to-many relationship between Customer and Order tables you described.
Assumptions About Table Structure
First, let's confirm the table fields (adjust if your actual schema differs slightly):
Customer: Hascustomer_id(primary key) andname(the field we need to retrieve)Order: Hasorder_id(primary key),customer_id(foreign key linking toCustomer), andpayment_status(only 'PAID' or 'UNPAID')
Approach 1: LEFT JOIN + Aggregation
This method uses a left join to include all customers, then groups them to check order statuses:
SELECT c.name FROM Customer c LEFT JOIN `Order` o ON c.customer_id = o.customer_id GROUP BY c.customer_id, c.name HAVING -- Customers who never placed an order (no associated order records) COUNT(o.order_id) = 0 OR -- Customers with all orders paid: at least one order, and no unpaid orders (COUNT(o.order_id) > 0 AND SUM(CASE WHEN o.payment_status = 'UNPAID' THEN 1 ELSE 0 END) = 0);
How This Works:
LEFT JOINensures we include every customer from theCustomertable, even those with no orders.GROUP BYclusters results by individual customer so we can aggregate their order data.- The
HAVINGclause filters for our two target groups:COUNT(o.order_id) = 0: No orders linked to the customer → fits the "never ordered" group.- The second condition checks that the customer has at least one order, and that there are zero unpaid orders in their history → fits the "all orders paid" group.
Approach 2: Using NOT EXISTS (Optimized for Speed)
Many database optimizers perform extremely well with NOT EXISTS (it uses semi-joins to avoid unnecessary data processing). This approach checks for the absence of specific order conditions:
SELECT c.name FROM Customer c WHERE -- Customers who never placed any orders NOT EXISTS (SELECT 1 FROM `Order` o WHERE o.customer_id = c.customer_id) OR -- Customers with no unpaid orders (all orders are paid) NOT EXISTS (SELECT 1 FROM `Order` o WHERE o.customer_id = c.customer_id AND o.payment_status = 'UNPAID');
How This Works:
- The first
NOT EXISTSchecks that there are no orders linked to the customer. - The second
NOT EXISTSchecks that there are no unpaid orders for the customer (this automatically includes customers with no orders, but combining it with the first condition viaORensures we cover both groups clearly).
Example Test Case
Let's use sample data to verify:
Customer Table
| customer_id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Charlie |
| 4 | Dave |
Order Table
| order_id | customer_id | payment_status |
|---|---|---|
| 101 | 1 | PAID |
| 102 | 1 | PAID |
| 103 | 2 | UNPAID |
| 104 | 3 | PAID |
Expected Result
| name |
|---|
| Alice |
| Charlie |
| Dave |
Both queries will return these names: Alice and Charlie have all orders paid, Dave never ordered, and Bob is excluded due to an unpaid order.
内容的提问来源于stack exchange,提问作者Rakesh

