简体   繁体   中英

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

I created an API for an accounting application. 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". 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.

While we're at it, we should clean up the SQL Injection vulnerability. We never want to include potentially unsafe values in SQL text.

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. (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.)

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. But we can pass the $year and $month values into the query, and have MySQL convert that to a date for us.

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

# 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:

# 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

$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.

You can use CASE-THEN-ELSE construction, try to execute this SQL statement:

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:

Your code is vulnerable for SQL Injection attacks. You MUST NOT pass SQL query parameters using strings concatenation. Use PDO instead and bind arguments.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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