我首先编写了一些子查询,然后将它们拼凑在一起。我个人的建议是,在寻求整个解决方案之前,始终将问题分解为更小的部分。
例如,我需要知道的一件事是每个客户尚未购买的所有产品。我通过交叉连接客户和产品表(以获取所有配对)并删除订单表中已经存在的配对来做到这一点,如下所示:
-- Get all customer/product pairings where customer_product
-- does not exist in orders table
SELECT c.id, p.id
FROM customer c
CROSS JOIN product p
WHERE (c.id, p.id) NOT IN (SELECT * FROM orders)
ORDER BY c.id;
我还编写了一个子查询来获取每个产品的平均评分。如果产品没有评分,此查询将返回 null:
SELECT p.id, AVG(r.rate) AS averageRating
FROM product p
LEFT JOIN rate r ON r.product_id = p.id
GROUP BY p.id;
现在,我可以将这两个作为子查询包含在内,并选择客户 ID、产品 ID 以及他们尚未购买的每种产品的评分:
SELECT t1.customerID, t1.productID, t2.averageRating
FROM(
SELECT c.id AS customerID, p.id AS productID
FROM customer c
CROSS JOIN product p
WHERE (c.id, p.id) NOT IN (SELECT * FROM orders)
ORDER BY c.id) t1
JOIN(
SELECT p.id AS productID, AVG(r.rate) AS averageRating
FROM product p
LEFT JOIN rate r ON r.product_id = p.id
GROUP BY p.id) t2 ON t2.productID = t1.productID;
那是最难的部分。剩下要做的唯一事情是进行一些聚合,以从每个客户未购买的商品中获得最大评分,然后在最大评分与平均评分匹配的条件下将该聚合查询与上面的查询连接起来。所以,这是我汇总的可怕查询:
SELECT t1.customerID, t1.productID, t1.averageRating
FROM(
SELECT t1.customerID, t1.productID, t2.averageRating
FROM(
SELECT c.id AS customerID, p.id AS productID
FROM customer c
CROSS JOIN product p
WHERE (c.id, p.id) NOT IN (SELECT * FROM orders)
ORDER BY c.id) t1
JOIN(
SELECT p.id AS productID, AVG(r.rate) AS averageRating
FROM product p
LEFT JOIN rate r ON r.product_id = p.id
GROUP BY p.id) t2 ON t2.productID = t1.productID) t1
JOIN(
SELECT t1.customerID, MAX(t2.averageRating) AS maxRating
FROM(
SELECT c.id AS customerID, p.id AS productID
FROM customer c
CROSS JOIN product p
WHERE (c.id, p.id) NOT IN (SELECT * FROM orders)
ORDER BY c.id) t1
JOIN(
SELECT p.id AS productID, AVG(r.rate) AS averageRating
FROM product p
LEFT JOIN rate r ON r.product_id = p.id
GROUP BY p.id) t2 ON t2.productID = t1.productID
GROUP BY t1.customerID) t2 ON t2.customerID = t1.customerID AND t2.maxRating = t1.averageRating
ORDER BY t1.customerID;
下面是 MySQL 工作台结果的快照:
需要注意的重要一点是,我没有消除联系。因此,例如,客户 2 没有购买产品 1 或 2,并且他们的评分相同,因此返回两行。
我在 MySQL 中进行了测试,因为 SQL Fiddle 不工作,但我让它工作了,所以这里有一个 Fiddle 示例,如果你愿意的话。