简体   繁体   English

如何在一列中获取所有值mysql的总和和减去

[英]how to get all value mysql sum and subtract in one column

I created an API for an accounting application. 我为会计应用程序创建了一个API。 I have income and spending in one column. 我在一栏中有收入和支出。 How can I sum all the income and spending and subtract? 如何将所有收入和支出相加并减去?

This my code: 这是我的代码:

private function list_recap() {

    if($this->get_request_method() != "GET") {
        $this->response('', 406);
    }

    $year  = $this->_request['year'];
    $month = $this->_request['month'];

    $sqlPlus = $this->db->query("SELECT SUM(nominal) 
            FROM lap_keuangan 
                WHERE YEAR(tanggal) = '$year' 
                AND MONTH(tanggal) = '$month' 
           AND kategori = 1");

    $sqlMin  = $this->db->query("SELECT SUM(nominal) 
            FROM lap_keuangan 
                WHERE YEAR(tanggal) = '$year' 
                AND MONTH(tanggal) = '$month' 
                AND kategori = -1");


    $result = array();
    while($rowPlus = $sqlPlus->fetch(PDO::FETCH_ASSOC)) {
        $plus = $result[] = $rowPlus;
    }

    while($rowMin = $sqlMin->fetch(PDO::FETCH_ASSOC)) {
        $minus = $result[] = $rowMin;
    }

    $this->response($this->json($result), 200);
}

Use one SELECT statement to retrieve the "plus", "minus" and "total". 使用一个SELECT语句检索“加”,“减”和“总计”。 We can use conditional aggregation. 我们可以使用条件聚合。 Given a condition in the WHERE clause that guarantees us that kategori will have a value of either 1 or -1, we can multiply nominal by that, and total that up with SUM. 给定WHERE子句中的一个条件,该条件可以保证kategori的值为1或-1,我们可以将nominal乘以该值,然后将其与SUM相加。

While we're at it, we should clean up the SQL Injection vulnerability. 在此过程中,我们应该清除SQL Injection漏洞。 We never want to include potentially unsafe values in SQL text. 我们从不希望在SQL文本中包含潜在的不安全值。

https://www.owasp.org/index.php/SQL_Injection https://www.owasp.org/index.php/SQL_Injection

We either properly escape the values before they are included, or we use prepared statement with bind placeholders. 我们要么在包含值之前适当地对它们进行转义,要么使用带有绑定占位符的预处理语句。 (It's not that hard; and there's no valid excuse not to do one or the other.) (这并不难;没有任何不做的有效借口。)

For performance, we prefer to have conditions on bare columns, rather than wrapping columns in expressions. 为了提高性能,我们希望在列上有条件,而不是在表达式中包装列。 If tanggal is DATE, DATETIME or TIMESTAMP datatype, we can specify condition on that column as a range of values. 如果tanggal是DATE,DATETIME或TIMESTAMP数据类型,我们可以在该列上指定条件作为值的范围。 (This allows MySQL to make use of a range scan operation if a suitable index is available, rather than evaluating expressions on every row in the table.) (如果合适的索引可用,这允许MySQL使用范围扫描操作,而不是对表中的每一行求值。)

I'd probably convert the $year and $month values into a yyyy-mm-01 formatted string, and then pass that date string into the query. 我可能会将$year$month值转换为yyyy-mm-01格式的字符串,然后将该日期字符串传递到查询中。 But we can pass the $year and $month values into the query, and have MySQL convert that to a date for us. 但是我们可以将$ year和$ month值传递给查询,并让MySQL将其转换为日期。

Something like this: 像这样:

# static SQL text (no variable interpolation) mitigates SQL Injection 
$sql = 
"SELECT SUM( k.nominal * IF(k.kategori=  1 ,1,0) )  AS kat_plus
      , SUM( k.nominal * IF(k.kategori= -1 ,1,0) )  AS kat_minus 
      , SUM( k.nominal * k.kategori              )  AS kat_total
   FROM lap_keuangan k 
  WHERE k.tanggal >= STR_TO_DATE(CONCAT( ? ,'-', ? ,'-01'),'%Y-%m-%d')
    AND k.tanggal <  STR_TO_DATE(CONCAT( ? ,'-', ? ,'-01'),'%Y-%m-%d') + INTERVAL 1 MONTH
    AND k.kategori IN (-1,1)";

# prepare
$sth = $this->db->prepare($sql);

either use bindValue to supply values for each bind placeholder and execute 使用bindValue为每个绑定占位符提供值并执行

# execute
$sth->bindValue(1, $year    ,PDO::PARAM_STR);
$sth->bindValue(2, $month   ,PDO::PARAM_STR);
$sth->bindValue(3, $year    ,PDO::PARAM_STR);
$sth->bindValue(4, $month   ,PDO::PARAM_STR);
$sth->execute(); 

or with positional binds, we can skip the bindValue and just pass an array to execute: 或使用位置绑定,我们可以跳过bindValue并只传递一个数组来执行:

# execute
$sth->execute(array($year,$month,$year,$month));

then fetch the row, and access the array members 然后获取该行,并访问数组成员

$row = $sth->fetch(PDO::FETCH_ASSOC);

$kat_plus  = $row['kat_plus'];
$kat_minus = $row['kat_minus'];
$kat_total = $row['kat_total'];

If PDO isn't/hasn't been configured to throw exceptions ie 如果未/未配置PDO引发异常,即

$this->$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

then add checks for success/failure of each PDO function call (prepare, execute, fetch) and handle the errors in the code. 然后添加对每个PDO函数调用(准备,执行,获取)成功/失败的检查,并处理代码中的错误。

You can use CASE-THEN-ELSE construction, try to execute this SQL statement: 您可以使用CASE-THEN-ELSE构造,尝试执行以下SQL语句:

SELECT
    SUM(CASE WHEN kategori = 1 THEN nominal ELSE 0 END) -
    SUM(CASE WHEN kategori = -1 THEN nominal ELSE 0 END)
FROM lap_keuangan
WHERE
    YEAR(tanggal) = '$year'
    AND MONTH(tanggal) = '$month'
    AND kategori = 1

PS: PS:

Your code is vulnerable for SQL Injection attacks. 您的代码容易受到SQL Injection攻击。 You MUST NOT pass SQL query parameters using strings concatenation. 您不得使用字符串串联传递SQL查询参数。 Use PDO instead and bind arguments. 请改用PDO并绑定参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM