分散的结果与总计

时间:2011-10-31 17:31:17

标签: php mysql

我一直在努力想出最好的方法来输出金融交易的总分的分页结果,首先是最近的交易,最后是第一个(最老的)交易,但不能似乎找到了一种有效的方法。

单独使用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>";
    }
}

再次感谢任何建议!

更新

这是我更新的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");

4 个答案:

答案 0 :(得分:4)

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

初始化变量:

SELECT @running := 0; 

主查询:

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

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

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

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

答案 1 :(得分:0)

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

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

祝你好运!

答案 2 :(得分:0)

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

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

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

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

答案 3 :(得分:0)

使用我从这个问题的其他答案中学到的一些东西(特别是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>";
        }
    }