MySQL:如何查找客户以不同顺序重复订购的产品

时间:2019-02-26 17:26:48

标签: mysql

我已经尝试了很多,但是当找不到解决方案时,我在这里发表了我的第一个问题:

我有四个表:

    products
    ---------------
    id, name

    customers
    ------------------
    id, name

    orders
    -------------
    id, customer_id

    orders_products
    ---------------------------
    id, order_id, product_id

当然,客户只能在订单中订购一次产品

在一个查询中,我想返回每个已经以多个订单订购任何产品的customer_id以及order_id和product_id。

换句话说,对于每个客户,我都希望查找一个客户多次订购的不同产品(在单独的订单中)以及product_id和order_id。因此product_id / order_id应该在结果中重复一次,因为将在一个以上的订单中多次订购产品。我不想返回只订购一次产品的记录。

我希望我足够清楚。

我已经有示例数据和表格。编辑我的问题以发布sql创建数据库和表:

CREATE DATABASE IF NOT EXISTS `test`
USE `test`;


CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7;

INSERT INTO `products` (`id`, `name`) VALUES
    (1, 'Car'),
    (2, 'Bus'),
    (3, 'Truck'),
    (4, 'Fan'),
    (5, 'Photo'),
    (6, 'Watch');

CREATE TABLE IF NOT EXISTS `customers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6;

INSERT INTO `customers` (`id`, `name`) VALUES
    (1, 'Sukh'),
    (2, 'Sukh2'),
    (3, 'Ravi'),
    (4, 'Ravinder'),
    (5, 'Jas');


CREATE TABLE IF NOT EXISTS `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `customer_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8;


INSERT INTO `orders` (`id`, `customer_id`) VALUES
    (1, 1),
    (2, 2),
    (3, 1),
    (4, 2),
    (5, 5),
    (6, 4),
    (7, 1);

CREATE TABLE IF NOT EXISTS `order_products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_id` int(11) NOT NULL DEFAULT '0',
  `product_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7;

INSERT INTO `order_products` (`id`, `order_id`, `product_id`) VALUES
    (1, 1, 1),
    (2, 2, 1),
    (3, 1, 2),
    (4, 4, 1),
    (5, 3, 5),
    (6, 7, 5);


select p.name, c.name, o.id as order_id, c.id as customer_id, p.id as product_id
from customers c inner join orders o on c.id=o.customer_id
inner join order_products op on op.order_id=o.id
inner join products p on p.id=op.product_id order by order_id

sukh2已在order_id 2和4中订购了汽车(一个客户订购了不止一次的汽车),所以我想同时返回这两个记录, sukh已在order_id 3和7中订购了照片(一位顾客订购了不止一次的照片),因此这2张照片也应归还总计4条记录。

我尝试了以下查询的多种变体,但未获得正确的结果。我想我唯一想念的就是在子查询结果上加入了customer_id。所以我尝试在给定的查询下面,它似乎确实起作用。 @barmar的建议有所帮助。

SELECT p.name, c.name, o.id AS order_id, c.id AS customer_id, p.id AS product_id
FROM customers c
INNER JOIN orders o ON c.id=o.customer_id
INNER JOIN order_products op ON op.order_id=o.id
INNER JOIN products p ON p.id=op.product_id
INNER JOIN (
SELECT customer_id, p.id AS product_id
FROM customers c
INNER JOIN orders o ON c.id=o.customer_id
INNER JOIN order_products op ON op.order_id=o.id
INNER JOIN products p ON p.id=op.product_id
GROUP BY c.id, p.id
HAVING COUNT(p.id) > 1) q2 ON q2.product_id=p.id AND q2.customer_id=c.id

可以写得更好吗?很晚了如果结果不正确,我会尝试更多并发布。

感谢大家的帮助。

1 个答案:

答案 0 :(得分:0)

您可以将子查询中的联接表减少为ordersorder_products,因为它们已经保存了所有必需的信息(customer_idproduct_id)。您也可以使用COUNT(*)代替COUNT(p.id)

SELECT p.name, c.name, o.id AS order_id, c.id AS customer_id, p.id AS product_id
FROM customers c
INNER JOIN orders o ON c.id=o.customer_id
INNER JOIN order_products op ON op.order_id = o.id
INNER JOIN products p ON p.id = op.product_id
INNER JOIN (
    SELECT o.customer_id, op.product_id
    FROM  orders o 
    INNER JOIN order_products op ON op.order_id = o.id
    GROUP BY o.customer_id, op.product_id
    HAVING COUNT(*) > 1
) q2 ON q2.product_id = p.id AND q2.customer_id = c.id

这将返回与您的查询相同的结果。

name    product_id  name    customer_id     order_id
Car              1  Sukh2             2            2
Car              1  Sukh2             2            4
Photo            5  Sukh              1            3
Photo            5  Sukh              1            7

演示:https://www.db-fiddle.com/f/bq2WbLNGdKHGjowkR25VAr/0

但是,如果您可以使用稍有不同的数据格式,则可以避免使用GROUP_CONCAT()聚合函数进行子查询并与相同的表联接。

SELECT p.name, op.product_id, c.name, o.customer_id, GROUP_CONCAT(o.id) AS order_ids
FROM  orders o 
INNER JOIN order_products op ON op.order_id = o.id
INNER JOIN customers c ON c.id = o.customer_id
INNER JOIN products p on p.id = op.product_id
GROUP BY o.customer_id, op.product_id
HAVING COUNT(*) > 1

结果:

name    product_id  name    customer_id     order_ids
Photo            5  Sukh              1     7,3
Car              1  Sukh2             2     4,2

演示:https://www.db-fiddle.com/f/n1zvoPjpBVAW9yvjPH1fro/0

但是为了获得更好的性能,在子查询中进行聚合仍然有意义,从而减小了需要为GROUP BY排序的行的大小。所以我可能会这样写查询:

SELECT p.name, q.product_id, c.name, q.customer_id, q.order_ids
FROM (
    SELECT o.customer_id, op.product_id, GROUP_CONCAT(o.id) order_ids
    FROM  orders o 
    INNER JOIN order_products op ON op.order_id = o.id
    GROUP BY o.customer_id, op.product_id
    HAVING COUNT(*) > 1
) q
INNER JOIN products p ON p.id = q.product_id
INNER JOIN customers c ON c.id = q.customer_id

演示:https://www.db-fiddle.com/f/v7xXW1cLgwAUR4DSGAomev/0