我正在尝试生成一个从下面的数据库中计算保证金的报告。问题是产品的成本(存在于purchase_order_products表中)可能会发生变化。
2017-06-08的ID为4022的产品成本为1110,但2017-07-25的成本为1094。这令人困惑。我无法获得每种产品的确切成本。
我写了一个PHP算法,它循环遍历所有订单和采购订单,并使用最旧的成本到最新的成本。但该算法具有非常高的时间复杂度。这可以使用mysql查询吗?
请查看以下情况:
公司为产品X创建了采购订单:数量3,第1天成本10.
客户购买2件产品X售价:第1天12件(库存中仍有1件,成本10件)
公司为产品X创建了采购订单:数量4,第2天成本9.
客户购买3件产品X售价:第2天12件
客户买了2个产品X卖价:第3天12个
公司为产品X创建了采购订单:数量2,第3天的成本11.
客户买了2个产品X卖价:第3天12个
报告:
第1天: 售出2件产品X为12件,成本10件,利润:2 *(12 - 10)
第2天: 出售3件产品X为12件,1件成本为10件,2件成品为9件,
利润:1 *(12 - 10)+ 2 *(12 - 9)
第3天: 出售2个产品X为12,成本9,利润:2 *(12 - 9)
售出2件产品X为12件,成本为11件,利润为:2 *(12 - 11件)
因此,新销售产品的利润使用相应的成本计算。希望你明白我的意思。
数据库中的4个产品
产品上述产品的采购订单
答案 0 :(得分:6)
为什么不放轻松并在订单表中添加一个利润列,该列表是在客户购买产品时实时计算的。这样,您可以仅根据给定的销售订单计算您的损失。事实上,这是为了产生销售价格已经以某种方式计算出来的。当然,这仅适用于未来的销售,但您可以使用现有代码进行少量修改,以填充旧记录的利润列,并且在更新之前,您只需为旧交易运行此代码一次。
详细说明:
更改表"sales_order"
添加"profit"
列。这样,您可以使用其他相关列(total_paid, total_refund, total_due, grand_total)
来计算总和,因为您可能希望通过在计算中根据需要包含这些货币字段来更好地控制报表,例如使用total_payed
生成报表仅排除tota_due
或将其包含在不同类型的报告中,换句话说,您只能从此表生成多种报告类型,而不会通过仅添加此列来压倒数据库系统。
的 编辑: 强>
您还可以向此表添加成本列以便快速检索,并最大限度地减少对其他表的连接和查询,如果您想更进一步,可以为报告添加专用表,这将非常有用,例如生成上个月缺少报告并检查旧订单状态。
答案 1 :(得分:3)
一些免责声明:
话虽如此,我认为这样的事情对你有用:
$productsIds = array('4022', '4023', '4160', '4548', '4601');
foreach($productIds as $pid){
$sql = "SELECT (soi.revenue - sum(pop.cost)) AS profit, sum(pop.cost) AS total_cost, sum(pop.quantity) AS total_purchased, soi.revenue, soi.total_sold
FROM purchase_order_products pop
JOIN (SELECT sum(price) AS revenue, sum(quantity_ordred) AS total_sold FROM sales_order_item WHERE product_id = ".$pid.") AS soi ON soi.product_id = pop.product_id
WHERE pop.product_id = ".$pid." GROUP BY pop.product_id HAVING sum(pop.quantity) < soi.total_sold ORDER BY pop.created_at ASC;";
$conn->query($sql);
//do what you want with results
}
这里的关键是在GROUP BY之后使用HAVING子句来确定在哪里切断查找购买成本的总和。只要它们在该范围内,您就可以对它们进行求和,并且您可以通过created_at获得正确的日期排序。
同样,我无法测试这一点,我不建议按原样使用此代码,只是希望这有助于“这里有一个关于如何实现这一点的一般概念”。
如果我有时间重新创建您的数据库,或者如果您提供带有示例数据的sql转储文件,我可以尝试为您提供一个有效的示例。
答案 2 :(得分:1)
给定时间内物体的价格由公式给出:“库存总价/库存总数”。
要实现此目的,您需要执行两个查询:
SQL:
SELECT SUM(row_total) sale_total_cost, SUM(quantity_ordered) sale_total_number
FROM sales_order_item soi
JOIN sales_order so ON soi.sales_order_id=so.id
WHERE so.purchase_date<'2017-06-07 15:03:30'
AND soi.product_id=4160;
SQL:
SELECT SUM(pop.cost * pop.quantity) purchase_total_price, SUM(pop.quantity) purchase_total_number
FROM purchase_order_products pop
JOIN purchase_order po ON pop.purchase_order_id=po.id
WHERE po.created_at<'2017-06-07 15:03:30'
AND pop.product_id=4160;
2017-02-01 14:23:35
处的产品4160的价格为:(purchase_total_price - sale_total_cost)/(purchase_total_number - sale_total_number)
问题是您的“sales_order”表格从2017-02-01 14:23:35
开始,而“purchase_order”表格从2017-06-07 08:55:48
开始。因此,只要您无法从一开始就跟踪所有购买,结果就会不连贯。
编辑:
purchase_order_products
表您必须修改purchase_order_products
以获得每种产品的消费:
ALTER TABLE `purchase_order_products` ADD COLUMN sold_items INT DEFAULT 0;
为了使其起作用,您必须使sold_items列反映您的真实库存
您应该使用以下请求初始化您的表
UPDATE `purchase_order_products` SET sold_items=quantity;
然后手动更新每个产品的确切库存表(这意味着quantity_ordered-sold_items必须反映您的实际库存。
这只需要进行一次。
ALTER TABLE sales_order_item ADD total_purchase_price INT DEFAULT NULL
当您输入新的销售订单时,您必须使用以下命令获取包含剩余料品的最早的采购订单:
SELECT * FROM `purchase_order_products` WHERE quantity!=sold_items where product_id=4160 ORDER BY `purchase_order_id` LIMIT 1;
然后,您将增加sold_items值,并计算总购买价格(总和)以填充total_purchase_price列。
可以使用sales_order_item表格中row_total和total_purchase_price之间的差异轻松计算保证金
答案 3 :(得分:1)
我感谢您使用FIFO方法管理库存。但是,这并不意味着您需要使用FIFO来计算边距。文章https://en.wikipedia.org/wiki/Inventory_valuation概述了这些选项。 (您所在国家的法规可能会排除某些选项。)
我认为单个销售的FIFO保证金计算的可重复解决方案很复杂。期初余额,退货,部分交货,分批装运,无序处理,库存调整,货物损坏等都很复杂。
您的问题中的数据库结构似乎没有解决这些问题。
通常,通过计算期间库存价值的变化来计算期间(日,月等)的保证金/利润来解决这些问题。
如果您可以使用平均成本方法,则可以使用纯SQL计算保证金。我相信其他方法似乎需要一些迭代,因为SQL中没有固有的顺序。 (您可以通过创建新表并存储以前的期间值来提高性能。)
我不会太担心将整个解决方案放在SQL中,因为这似乎不会降低问题的计算复杂性。在数据库引擎中进行尽可能多的计算仍然可能具有速度优势,特别是在数据集很大的情况下。
您可能会发现这篇文章很有趣:Set-based Speed Phreakery: The FIFO Stock Inventory SQL Problem。 (那里有一些聪明的人!)