2

好的,所以我正面临这个极其复杂的问题,因为我不是 MySQL 的专家,所以我肯定需要你的意见。

假设我们有一个数据库,使用下面的代码创建(我正在粘贴创建代码 - 只是绝对必要的表 - 以避免粘贴所有表):

DROP TABLE IF EXISTS `Jeweller`.`Orders`;
CREATE TABLE `Jeweller`.`Orders` (
  `id` int(11) unsigned NOT NULL,
  `date` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `Jeweller`.`Product_categories`;
CREATE TABLE `Jeweller`.`Product_categories` (
  `id` int(11) unsigned NOT NULL,
  `name` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `Jeweller`.`Product_orders`;
CREATE TABLE `Jeweller`.`Product_orders` (
  `order_id` int(11) unsigned NOT NULL,
  `product_id` int(11) unsigned NOT NULL,
  `quantity` int(11),
  `value` float,
  FOREIGN KEY (`order_id`) REFERENCES `Jeweller`.`Orders`(`id`),
  FOREIGN KEY (`product_id`) REFERENCES `Jeweller`.`Products`(`id`),
  CHECK (`quantity`>0),
  CHECK (`value`>0)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `Jeweller`.`Product_returns`;
CREATE TABLE `Jeweller`.`Product_returns` (
  `sale_id` int(11) unsigned NOT NULL,
  `product_id` int(11) NOT NULL,
  `date` date DEFAULT NULL,
  `quantity` int(11),
  `value` float,
  FOREIGN KEY (`sale_id`) REFERENCES `Jeweller`.`Sales`(`id`),
  FOREIGN KEY (`product_id`) REFERENCES `Jeweller`.`Products`(`id`),
  CHECK (`quantity`>0),
  CHECK (`value`>0)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `Jeweller`.`Product_sales`;
CREATE TABLE `Jeweller`.`Product_sales` (
  `sale_id` int(11) unsigned NOT NULL,
  `product_id` int(11) NOT NULL,
  `quantity` int(11),
  `value` float,
  FOREIGN KEY (`sale_id`) REFERENCES `Jeweller`.`Sales`(`id`),
  FOREIGN KEY (`product_id`) REFERENCES `Jeweller`.`Products`(`id`),
  CHECK (`quantity`>0),
  CHECK (`value`>0)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `Jeweller`.`Products`;
CREATE TABLE `Jeweller`.`Products` (
  `id` int(11) unsigned NOT NULL,
  `product_category_id` int(11) NOT NULL,
  `seller_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  `description` text,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`product_category_id`) REFERENCES `Jeweller`.`Product_categories`(`id`),
  FOREIGN KEY (`seller_id`) REFERENCES `Jeweller`.`Sellers`(`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `Jeweller`.`Sales`;
CREATE TABLE `Jeweller`.`Sales` (
  `id` int(11) unsigned NOT NULL,
  `date` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

现在,假设我们将利润定义为:

  • Sales - Returns - Orders

您将如何进行查询以获取:

按月和 product_category 计算的利润,仅适用于 2013 年。


出于测试目的,这里是完整的数据库创建代码以及数据库人口代码(带有一些演示数据)。(SQLFiddle 链接


附言

  • 实际的代码有点不同(上面只是一个例子——虽然是 100% 忠诚的)

  • 经过几次尝试,我设法过滤了 2013 年的销售/订单/等...我什至设法按产品获得利润(尽管它花了一些无穷无尽join的 s、left outer joins 等...哈哈)...但是这个看起来要复杂得多。有任何想法吗?

4

2 回答 2

1

简单来说,如果重组是不可取的,那么我会做一个简单的查询来分别确定订单、退货和销售的价值,然后将它们连接在一起。这可以使用 UNION 和子查询来完成,如下例所示:SQLFiddle

我还冒昧地将 FLOAT 换成了 DECIMAL。索引等可能有改进的余地,但这应该使您能够很好地确定总和。如果您查看子查询,您会看到 ORDER 和 RETURN 选择根据您的要求选择负值。

一个潜在的缺陷是不会包含已删除 Product 记录的任何记录。这可以通过将Product连接更改为 LEFT JOIN 并适当处理 product_category_id 的 NULL 值来避免。决定将此添加到最新示例中,但如果从不删除行Product则INNER JOIN 就足够了

SELECT

  d.thisMonth,
  d.product_category_id,
  SUM(d.sumValue)

FROM (

  (
    -- Get the order value

    SELECT

      'order' AS valueType,
      MONTH(o.date) AS thisMonth,
      p.product_category_id,
      SUM(-po.value * po.quantity) AS sumValue

    FROM Orders o

    INNER JOIN Product_orders po
    ON po.order_id = o.id

    LEFT JOIN Products p
    ON p.id = po.product_id

    WHERE o.date BETWEEN '2013-01-01' AND '2013-12-31'

    GROUP BY
        thisMonth,
        product_category_id

  ) UNION ALL (

    -- Get the sales value

    SELECT

      'sale' AS valueType,
      MONTH(s.date) AS thisMonth,
      p.product_category_id,
      SUM(ps.value * ps.quantity) AS sumValue

    FROM Sales s

    INNER JOIN Product_sales ps
    ON ps.sale_id = s.id

    INNER JOIN Products p
    ON p.id = ps.product_id


    WHERE s.date BETWEEN '2013-01-01' AND '2013-12-31'

    GROUP BY
        thisMonth,
        product_category_id

  ) UNION ALL (

    -- Get the return value

    SELECT

      'return' AS valueType,
      p.product_category_id,
      MONTH(pr.date) AS thisMonth,
      SUM(-pr.value * pr.quantity) AS sumValue

    FROM Product_returns pr

    INNER JOIN Products p
    ON p.id = pr.product_id

    WHERE pr.date BETWEEN '2013-01-01' AND '2013-12-31'

    GROUP BY
        thisMonth,
        product_category_id

  )
) d
GROUP BY 
  d.thisMonth,
  d.product_category_id;
于 2013-06-11T12:28:09.890 回答
1

这是您的架构的近似值...

DROP TABLE IF EXISTS orders;
CREATE TABLE orders 
( order_id int(11) unsigned NOT NULL auto_increment
, date date DEFAULT NULL
, PRIMARY KEY (order_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO orders VALUES
(NULL,'2013-01-01'),
(NULL,'2013-01-01'),
(NULL,'2013-02-02'),
(NULL,'2013-02-03'),
(NULL,'2013-03-05'),
(NULL,'2013-06-07');

DROP TABLE IF EXISTS product_orders;
CREATE TABLE product_orders 
( order_id int unsigned NOT NULL
, product_id int unsigned NOT NULL
, quantity int NOT NULL DEFAULT 1
, value DECIMAL(5,2)
, PRIMARY KEY(order_id,product_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO product_orders VALUES
(1,101,1,100),
(1,102,1,50),
(2,101,2,200),
(3,101,1,100),
(4,102,2,100),
(4,103,3,150),
(5,104,1,300),
(6,102,1,50),
(6,103,2,100),
(6,104,1,300);

DROP TABLE IF EXISTS product_returns;
CREATE TABLE product_returns 
( sale_id int unsigned NOT NULL
, product_id int NOT NULL
, date date DEFAULT NULL
, quantity int NOT NULL DEFAULT 1
, value DECIMAL(5,2)
, PRIMARY KEY(sale_id,product_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO product_returns VALUES
(21,101,'2013-01-04',2,200),
(22,102,'2013-03-06',1,50),
(22,103,'2013-05-08',1,50),
(23,104,'2013-06-09',1,300);


DROP TABLE IF EXISTS product_sales;
CREATE TABLE product_sales 
( sale_id int unsigned NOT NULL
, product_id int NOT NULL
, quantity int NOT NULL
, value DECIMAL(5,2)
, PRIMARY KEY(sale_id,product_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO product_sales VALUES
(20,101,1,100),
(20,102,1,50),
(21,101,3,300),
(22,101,1,100),
(22,102,2,100),
(22,103,1,50),
(23,103,2,100),
(23,104,2,600);


DROP TABLE IF EXISTS products;
CREATE TABLE products 
( product_id int unsigned NOT NULL AUTO_INCREMENT
, product_category_id int NOT NULL
, name varchar(100) NOT NULL
, description text NULL
, PRIMARY KEY (product_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO products VALUES
(101,1,'donuts','Mmm, donuts'),
(102,2,'buzz Cola','Mmm, donuts'),
(103,2,'duff beer','Can\'t get enough'),
(104,1,'Krusty-O\'s','Yum, yum');

DROP TABLE IF EXISTS sales;
CREATE TABLE sales
( sale_id int NOT NULL
, date date DEFAULT NULL
, PRIMARY KEY (sale_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO sales VALUES
(20,'2013-01-12'),
(21,'2013-02-15'),
(22,'2013-03-17'),
(23,'2013-05-18');

...和一个可能的查询...

SELECT p.product_category_id
     , MONTH(date) month
     , SUM(value) profit
  FROM 
     ( SELECT product_id,value, date 
         FROM product_sales ps
         JOIN sales s
           ON s.sale_id = ps.sale_id
        UNION ALL
       SELECT product_id,value*-1,date FROM product_returns
        UNION ALL
       SELECT product_id,value*-1,date
         FROM product_orders po
         JOIN orders o
           ON o.order_id = po.order_id
     ) x
  JOIN products p
    ON p.product_id = x.product_id
 WHERE YEAR(date) = 2013
 GROUP
    BY p.product_category_id
     , MONTH(date);

+---------------------+-------+---------+
| product_category_id | month | profit  |
+---------------------+-------+---------+
|                   1 |     1 | -400.00 |
|                   1 |     2 |  200.00 |
|                   1 |     3 | -200.00 |
|                   1 |     5 |  600.00 |
|                   1 |     6 | -600.00 |
|                   2 |     1 |    0.00 |
|                   2 |     2 | -250.00 |
|                   2 |     3 |  100.00 |
|                   2 |     5 |   50.00 |
|                   2 |     6 | -150.00 |
+---------------------+-------+---------+

...和一个相同的 sqlfiddle:http ://www.sqlfiddle.com/#!2/22a1d/1

于 2013-06-11T12:37:57.680 回答