【问题标题】:Aggregate calculation query on single SQL table对单个 SQL 表的聚合计算查询
【发布时间】:2014-03-18 13:01:36
【问题描述】:

在我的数据库中,我有一个表 payers_payments,由一些外键(payer_idpayment_id)和一些数据透视字段(amountpays)组成。

| id | payer_id | payment_id | amount | pays |
|----|----------|------------|--------|------|
| 1  | 1        | 1          | 20     | 0    |
| 2  | 2        | 1          | 23     | 1    |
| 4  | 1        | 2          | 14     | 1    |
| 5  | 2        | 2          | 17     | 1    |
| 6  | 1        | 3          | 10     | 1    |
| 7  | 2        | 3          | 0      | 0    |

每一行代表payerpayment 所做的贡献。 pays 字段指定是否应将 payer 包含在付款中。

payment 的总成本是给定payment_idamount 的总和。因此,如果我想看看我会运行多少付款 2:

SELECT SUM(amount) AS sumOfPayment 
  FROM payers_payments  
 WHERE payment_id=2;

同样,给定的payer(例如付款人 1)贡献的总金额为:

SELECT SUM(amount) AS sumOfPayment 
  FROM payers_payments 
 WHERE payer_id=1;

现在,我想做的是使用公平分享的概念。公平份额是付款的总成本除以拥有pay=1payers 的数量。我可以表达这一点的最简单方法是使用子查询:

SELECT SUM(payers_payments.amount) / (SELECT count(*) 
                                        FROM payers_payments 
                                       WHERE pays 
                                             AND payers_payments.payment_id = 3
                                             ) AS FairShare
  FROM payers_payments
 WHERE payment_id=3
GROUP BY 
       payers_payments.payment_id;

对于给定的payerpayment,它们的公平份额定义为:

SELECT IF(pays, FairShare, 0) as payerFairShare 
  FROM payers_payments 
 WHERE payer_id = 1 
       AND payment_id=3; --FairShare is the query as above

我的问题是,我希望根据每笔付款的公平份额以及它们是否包含在付款中,查询每个 payer 的总公平份额。 (如果pays=false 那么他们的公平份额是0)

根据上述数据,这是我为 2 个付款人所追求的结果:

| payer_id | total_paid | fair_share |
|----------|------------|------------|
| 1        | 44         | 25.5       |
| 2        | 40         | 58.5       |

有没有办法在单个查询中实现这一点,还是我必须对结果集进行一些循环? 我对 RDMS 不了解,但类似 ​​MySQL 的东西很好。

【问题讨论】:

  • 应该可以使用子查询。

标签: mysql sql


【解决方案1】:

我将首先编写一个查询单笔付款是多少的查询。也就是说,根据payment_id,所有金额的总和除以需要支付的人数。然后可以将该结果连接回原始数据。

SELECT
  payers_payments.payer_id,
  SUM(payers_payments.amount                      )   AS total_paid,
  SUM(payers_payments.pays * payments.single_share)   AS fair_share
FROM
  payers_payments
INNER JOIN
(
  SELECT
    payment_id,
    SUM(amount) / SUM(pays)   AS single_share
  FROM
    payers_payments
  GROUP BY
    payment_id
)
  AS payments
    ON  payers_payments.payment_id = payments.payment_id
GROUP BY
   payers_payments.payer_id

(payment_id)(payer_id) 上都有索引会很有用。

在 DECIMAL 数据类型中包含 amount 字段将是有益的,但您需要考虑要对舍入执行什么操作。 (一共支付10.00需要分三份,每份3.33,剩下的0.01你想怎么办?)

【讨论】:

  • 哇,干得好。那么查询创建了一个不存在的表payments 来保存单股?我已经有一个payments 表,所以我想把它叫做别的东西是有意义的,但它确实有效!金额存储为小数点后大于 2 位的小数,因此就四舍五入而言,视图中的金额四舍五入为 2 dp,因此我目前不介意出现轻微错误。数周以来一直在尝试获得此功能,因此感谢您的帮助。
  • @harryg - 它实际上并没有创建表,它只是一个子查询。类似于仅在查询期间存在的视图。但选择不冲突的名称通常是有助于维护的好习惯。
  • @harryg - 请注意将金额保留在小数点后 2 位以上,以“绕过”舍入错误。随着时间的推移,它会导致钱慢慢“消失”。在10.00 / 3 的情况下,一种选择是给两个人3.33 和一个人3.34。另一种是创建一个“基金”,收集备用的0.01 以某种方式赠送。但通常认为允许人们拥有3.333333(因为不能拥有 0.003333 的货币)同时还允许0.000001“从帐户中消失”是非常糟糕的做法.
  • 公平点,感谢您的建议。我将在我的应用程序中实现这个查询(需要尝试在 Laravel/Eloquent 中复制查询)。一旦我对事情有了头绪,我就会进行四舍五入。由于数据是动态计算的(即不存储结果),我假设舍入可以双向工作,并且会随着时间的推移而均匀。
  • @harryg - 这是一个危险的假设,许多会计师会和你一起度过一天。但是根据您可能无关紧要的上下文,我不知道。也就是说,我同意让它工作作为第一步非常重要;)祝你好运。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-16
  • 2010-09-21
  • 2019-02-03
  • 1970-01-01
  • 2011-11-27
相关资源
最近更新 更多