从SQL查询创建发票而不将查询放入循环中

时间:2016-03-16 04:50:16

标签: php mysql

我的公司有一个简单的发票系统,几年前我用PHP编写了一个MySQL数据库。当我写这篇文章时,我知道这很危险。虽然它确实有效,但很多代码的效率非常低,速度慢且构思不佳,所以我重新编写了一些代码,希望能够修复它。

有两个表:customerstransactions。当需要创建发票时,有两个查询,第二个不幸的是在while循环中。这是一些简化的伪代码,用于显示我正在做的事情:

// Get list of customers who owe something
SELECT name, address 
FROM customers 
WHERE 
   (SELECT COUNT(*) FROM transactions 
    WHERE transactions.customer_id = customers.id 
    AND owed > 0) 
  > 0
ORDER BY address

//loop through that result and query the transactions table to get a   
//list of charges for each customer. Like:

while ($row = customer_array()) {
    echo name_and_address;

    SELECT * FROM transactions WHERE id = $row['customer_id']
      AND owed = TRUE ORDER BY date

      while ($row = transactions_array()) {
        echo each_transaction_row;

}

很明显,循环中的嵌套循环,子查询和查询都很糟糕,更糟糕,更慢。我已尝试加入但似乎无法弄清楚如何使它们在此上下文中正常工作。做这样的事的正确方法是什么?可以用一个查询来完成吗?

表格结构:

CREATE TABLE `customer_data` (
`account_id` int(6) NOT NULL AUTO_INCREMENT,
`service_address` varchar(255) DEFAULT NULL,
`service_zipcode` int(5) DEFAULT NULL,
`first_name` varchar(255) DEFAULT NULL,
`last_name` varchar(255) DEFAULT NULL,
`billing_address1` varchar(255) DEFAULT NULL,
`billing_address2` varchar(255) DEFAULT NULL,
`billing_city` varchar(255) DEFAULT NULL,
`billing_state` varchar(2) DEFAULT NULL,
`billing_zipcode` varchar(20) DEFAULT NULL,
`phone1` varchar(100) DEFAULT NULL,
`phone2` varchar(100) DEFAULT NULL,
`quoted_price` int(6) DEFAULT NULL,
`current_price` int(6) DEFAULT NULL,
`original_interval` varchar(10) DEFAULT NULL,
`current_interval` varchar(7) DEFAULT NULL,
`temp_interval` int(5) NOT NULL,
`email` varchar(100) DEFAULT NULL,
`origin_date` varchar(20) DEFAULT NULL,
`remarks` text,
`crew_remarks` text NOT NULL,
`customer_type` varchar(10) DEFAULT NULL,
`perm_crew_assign` int(5) NOT NULL DEFAULT '0',
`temp_crew_assign` int(5) NOT NULL,
`date_last_service` date DEFAULT NULL,
`next_scheduled_date` date DEFAULT NULL,
`excluded_days` varchar(255) NOT NULL,
`special_instructions` text NOT NULL,
`inactive` tinyint(1) NOT NULL DEFAULT '0',
`location` varchar(255) NOT NULL,
`sent_letter` date NOT NULL,
`date_added` datetime NOT NULL,
`email_notify` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`account_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1687 DEFAULT CHARSET=utf8

CREATE TABLE `transactions` (
`transaction_id` int(10) NOT NULL AUTO_INCREMENT,
`account_id` int(6) NOT NULL,
`crew_id` int(3) NOT NULL,
`date_performed` date NOT NULL,
`date_recorded` datetime NOT NULL,
`price` int(6) NOT NULL,
`amount_paid` int(6) NOT NULL,
`description` varchar(255) NOT NULL,
`user` varchar(255) NOT NULL,
`status` int(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`transaction_id`)
) ENGINE=InnoDB AUTO_INCREMENT=69233 DEFAULT CHARSET=latin1

2 个答案:

答案 0 :(得分:1)

您可以使用一个查询和一个循环执行此操作。您可以加入这两个表并对结果进行排序。然后,当名称发生变化时,您就知道自己正在开始新客户。

SELECT c.name, c.address, t.*
FROM customers  AS c
JOIN transactions AS t ON (t.customer_id = c.customers.id)
WHERE c.owed > 0
ORDER BY `c`.`name`

// Data looks like this
// | customer.name | customer.address | transaction.* |

$prevName = null;
$prevCustTransList = [];
$prevCustTransTotalOwed = 0;
while ($row = result_array()) { // pseudo code
    if ( $row['c.name'] != $prevName ) {

        if ( $prevName != null  && $prevCustTransTotalOwed > 0 ) {
            echo $prevName; // pseudo code
            foreach($prevCustTransList as $t) {
                echo $t; // pseudo code
            }
        }

        $prevName = $row['c.name'];
        $prevCustTransTotalOwed = 0;
        $prevCustTransList = [];
    }
    // Keep the list of transactions to print out later.
    $prevCustTransList[] = transaction data; // pseudo code
    // Keep a running total of the amount owed
    // Replace with actual calculation
    $prevCustTransTotalOwed += transaction.price;
}

答案 1 :(得分:1)

是的,您可以在一个查询中执行此操作,按account_id排序。在您的交易中' table account_id应编入索引,因为您将使用它加入' customer_data'。

$st_date = '2016-01-01';
$end_date = '2016-02-01';
//select sql
SELECT c.name,c.account_id, ts.*
FROM customer_data c
INNER JOIN transactions ts ON (c.account_id = ts.account_id)
WHERE ts.price > 0 and ts.date_performed between $st_date and $end_data

$current_account_id = null;
while ($row = $results) {
   if ($current_account_id != $row['account_id']) {
      //print customer name, details, etc..
   }
   //print the transactions detail
   echo $results['transaction_id']; //etc...

   //keep track of the current customer
   $current_account_id = $row['account_id']
}