繁体   English   中英

分散的结果与总计

[英]Paginated results with a running total

我一直试图想出最好的方法来输出具有运行总额的金融交易的分页结果,从最近的交易开始到最后的第一个(最老的)交易,并且似乎找不到有效的方法来解决它。

仅使用OFFSETLIMIT拉取结果将不起作用,因为我试图显示运行总计。

出于绝望,我终于使用了一个多维数组,其中主数组中的每个数组都包含x个条目,并通过调用每个条目块来访问结果(例如, $transArr[0]将包含前38个记录, $transArr[1]接下来的38等等)。 我确信这是一种非常低效的处理方式,我会喜欢任何和所有的建议。

这是我提出的 - 抱歉,这是很多代码,包括分页链接和数据格式。 这只是一个类中的一个对象。

public function fetchTransactionsDev($currPage = null) {
    global $db;
    //Balance the account, set accountBalance variable
    $this->balanceAccount();
    $accountBalance = $this->accountBalance;
    $runningTotal = $accountBalance; //Start the Running Total as the balance
    $prevAmount = 0; //Starts at 0, will be used to calculate running total below

    //Fetch number of rows and calculate number of pages for paginated links
    $numRows = $db->query("SELECT COUNT(*) FROM transactions");
    $numRows = $numRows->fetchColumn();
    $this->totalTrans = $numRows;
    //Number of rows to display per page
    $rowsPerPage = 35;
    //Find out total pages, factoring in that the array starts at 0
    $totalPages = ceil($numRows / $rowsPerPage) - 1;
    //Get current page or set default
    if (isset($currPage) && is_numeric($currPage)) {
        $currentPage = (int) $currPage;
    } else {
        $currentPage = 0;
    }
    //Set $currPage to $totalPages if greater than total
    if ($currentPage > $totalPages) {
        $currentPage = $totalPages;
    }
    if ($currentPage < 1) {
        $currentPage = 0;
    }
    //Offset of the list, based on current page
    $offset = ($currentPage - 1) * $rowsPerPage;

    //Array to hold transactions; counters for number of arrays and number of entries per array
    $transArr = array();
    $arrCount = 0;
    $i = 0;

    //Fetch the transactions
    $sql = "SELECT amount, payee, cat, date FROM transactions ORDER BY id DESC, date DESC";
    $fetchTransactionsSQL = $db->query($sql);
    while ($transactionDetails = $fetchTransactionsSQL->fetch()) {
        $date = date("m/d", strtotime($transactionDetails['date']));
        $payee = stripslashes($transactionDetails['payee']);
        $category = $transactionDetails['cat'];
        $amount = $transactionDetails['amount'];
        $runningTotal -= $prevAmount;
        $amountOutput = money_format("%n", $amount);
        $runningTotalOutput = money_format("%n", $runningTotal);
        //Add new array to $transArr with a maximum of x num entries
        if ($i <= $rowsPerPage) {
            $transArr[$arrCount][] = array("date" => $date, "payee" => $payee, "category" => $category, 
            "amountOutput" => $amountOutput, "runningTotalOutput" => $runningTotalOutput);
            $i++;
        } else {
            //If over x number of entries, start a new array under $transArr and reset increment counter
            $arrCount++;
            $i = 0;
            $transArr[$arrCount][] = array("date" => $date, "payee" => $payee, "category" => $category, 
            "amountOutput" => $amountOutput, "runningTotalOutput" => $runningTotalOutput);;
        }
        if ($arrCount > $currentPage) {
            break;
        }
        $prevAmount = $amount; //Needed for calculating running balance
    }
    //Output the results to table
    foreach ($transArr[$currentPage] as $transaction) {
        echo "
            <tr>
                <td>{$transaction['date']}</td>
                <td><strong>{$transaction['payee']}</strong></td>
                <td>{$transaction['category']}</td>
                <td>{$transaction['amountOutput']}</td>
                <td>{$transaction['runningTotalOutput']}</td>
            </tr>                    
        ";
    }
    //Create paginated links
    if ($currentPage > 0) {
        $prevPage = $currentPage - 1;
        $this->pageLinks = "<a href='{$_SERVER['PHP_SELF']}?currentPage=$prevPage'>Prev</a>";
    }
    if ($currentPage != $totalPages) {
        $nextPage = $currentPage + 1;
        $runningBal = $runningTotal - $prevAmount;
        $this->pageLinks .= "  <a href='{$_SERVER['PHP_SELF']}?currentPage=$nextPage'>Next</a>";
    }
}

再次感谢任何建议!

UPDATE

这是我更新的SQL,提供了答案。 这显示正确的运行余额(运行余额=运行余额 - 上一个金额),但我试图创建分页结果。

$dough = new doughDev;
$dough->balanceAccount();
$accountBalance = $dough->accountBalance;
$setRunning = $db->query("SET @running := $accountBalance, @prevAmount = 0");
$getRunning = $db->query("SELECT amount, @running := @running - @prevAmount AS running, @prevAmount := amount AS prevAmount FROM transactions ORDER BY id DESC, date DESC");

这有点难看,但你可以让MySQL使用一些服务器端变量为你做运行总计。 您希望列出最近到最旧的交易的事实有点令人讨厌,但很容易处理:

初始化变量:

SELECT @running := 0; 

主查询:

SELECT amount, @running := @running + amount AS running, payee, cat, date
FROM ...
ORDER BY date ASC

它将以日期转发顺序计算runnign总数。 然后将其包装在另一个查询中,以反转排序顺序并应用limit子句

SELECT amount, running, payee, cat, date
FROM (
    ... previous query here ...
) AS temp
ORDER BY date DESC
LIMIT $entries_to_show, $offset

这有点效率低,因为内部查询将获取整个表,因此它可以计算运行总计,然后外部查询将丢弃除$ offset之外的所有行,以仅显示该“页面”。

您正在寻找的是MySQL的LIMIT和OFFSET功能。

如果你愿意,我很乐意提供一个例子。

祝好运!

我的第一个想法与@Marc B回答(+1)相同。 在查询中使用变量。

现在您可能不需要数据的同步视图。 如果您能够承担丢失一些最近的事务,您应该从第一个查询构建一个表 ,然后在表中添加运行总量。 您的请求成为此表上的简单分页请求(通过cron重建它?)。 临时表本来是一件好事,但是你在PHP中工作,临时表只在会话期间保持不变。 所以你会在每次浏览器请求后松开它。 MySQL 游标也不是持久的。 所以你应该为此建立一个真正的表。

更高级的解决方案是在原始表上的每个新事务成功之后填充此表表内容 ,这可以通过触发器完成,一个触发器在running_total表中记录事务,并且可能在此表上的某些触发器重新计算运行总计(插入/更新/删除)。

使用此解决方案, 您将失去简单SQL查询的过滤能力 (不容易过滤事务,您只有所有事务),但如果您有大量事务,变量解决方案在最后一页上会非常慢,当你读表时,这个很快。

使用我从这个问题的其他答案中学到的一些东西(特别是MySQL变量 - 感谢Marc B),我为这个问题制定了一个相当简单的解决方案。 对于初学者,部分分页链接代码包括通常用于MySQL查询的偏移量。 我不是在传统意义上使用它,而是将计数器设置为1,并在每次while循环运行时逐渐增加1。 当计数器达到偏移值时,我开始输出结果,当它到达偏移量+每页的总行数时结束。 当到达结束时,我打破了while循环。

它看起来很快。 如果有人想提出任何建议,请随意! 这是最终的代码:

public function fetchTransactions($currPage = null) {
        global $db;
        //Balance account
        $this->balanceAccount();
        $accountBalance = $this->accountBalance;
        //Fetch number of rows and calculate number of pages for paginated links
        $numRows = $db->query("SELECT COUNT(*) FROM transactions");
        $numRows = $numRows->fetchColumn();
        $this->totalTrans = $numRows;
        //Number of rows to display per page
        $rowsPerPage = 35;
        //Find out total pages
        $totalPages = ceil($numRows / $rowsPerPage);
        //Get current page or set default
        if (isset($currPage) && is_numeric($currPage)) {
            $currentPage = (int) $currPage;
        } else {
            $currentPage = 1;
        }
        //Set $currPage to $totalPages if greater than total
        if ($currentPage > $totalPages) {
            $currentPage = $totalPages;
        }
        if ($currentPage < 1) {
            $currentPage = 1;
        }
        //Offset of the list, based on current page
        $offset = ($currentPage - 1) * $rowsPerPage;
        //Set end point for records per page
        $end = $offset + $rowsPerPage;

        //Start counter for retrieving records for current page
        $i = 1;

        //Fetch the transactions
        $setRunning = $db->query("SET @running := $accountBalance, @prevAmount = 0"); //Sets varaible for calculating running total
        $sql = "SELECT amount, @running := @running - @prevAmount AS running, @prevAmount := amount AS prevAmount, payee, cat, date 
        FROM transactions ORDER BY id DESC, date DESC";
        $fetchTransactionsSQL = $db->query($sql);
        while ($transactionDetails = $fetchTransactionsSQL->fetch()) {
            $amount = $transactionDetails['amount'];
            //If counter reaches beginning of offset, start outputting results. End when the last row for result set is reached
            if ($i >= $offset && $i < $end) {
                $date = date("m/d", strtotime($transactionDetails['date']));
                $payee = stripslashes($transactionDetails['payee']);
                $category = $transactionDetails['cat'];
                $amountOutput = money_format("%n", $amount);
                $runningTotalOutput = money_format("%n", $transactionDetails['running']);
                echo "
                <tr>
                    <td>$date</td>
                    <td><strong>$payee</strong></td>
                    <td>$category</td>
                    <td>$amountOutput</td>
                    <td>$runningTotalOutput</td>
                </tr>                    
                ";
            }
            //If the end of the result set has been reached, break out of while loop. Else, increment the counter
            if ($i == $end) {
                break;
            } else {
                $i++;
            }
        }
        //Create paginated links
        if ($currentPage > 1) {
            $prevPage = $currentPage - 1;
            $this->pageLinks = "<a href='{$_SERVER['PHP_SELF']}?currentPage=$prevPage'>Prev</a>";
        }
        if ($currentPage != $totalPages) {
            $nextPage = $currentPage + 1;
            $runningBal = $runningTotal - $prevAmount;
            $this->pageLinks .= "  <a href='{$_SERVER['PHP_SELF']}?currentPage=$nextPage'>Next</a>";
        }
    }

暂无
暂无

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

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