[英]How to efficiently SUM and SUBTRACT from two conditionally linked tables using PHP and MySQL
Is there a more efficient solution for the below query. 对于以下查询是否有更有效的解决方案。 I have tried to research the topic to the best of my ability but it's difficult to know what to actually search for...
我尽力研究这个话题,但很难知道实际搜索的内容......
$tenant_balance = 0;
$total_charge_amount_query = mysqli_query($con, "
SELECT tenant_charge_id, tenant_charge_total_amount
FROM accounts_tenant_charge
WHERE tenant_charge_tenancy_id='{$tenancy_details['tenancy_id']}'"
) or die(mysql_error());
while($total_charge_amount_row = mysqli_fetch_array( $total_charge_amount_query )) {
$tenant_balance = $tenant_balance + $total_charge_amount_row['tenant_charge_total_amount'];
$total_payment_amount_query = mysqli_query($con, "
SELECT tenant_charge_payment_amount
FROM accounts_tenant_charge_payment
WHERE tenant_charge_payment_tenant_charge_id =
'{$total_charge_amount_row['tenant_charge_id']}"
) or die(mysql_error());
while($total_payment_amount_row = mysqli_fetch_array( $total_payment_amount_query )) {
$tenant_balance = $tenant_balance - $total_payment_amount_row['tenant_charge_payment_amount'];
}
}
echo '£' . number_format($tenant_balance, 2, '.', ',');
I have added the database table structure and some data below. 我在下面添加了数据库表结构和一些数据。
Table #1 表格1
-- phpMyAdmin SQL Dump
-- version 4.0.10.7
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Jun 16, 2015 at 01:50 PM
-- Server version: 5.1.73-cll
-- PHP Version: 5.4.23
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
--
-- Database: `propsyst_atlas`
--
-- --------------------------------------------------------
--
-- Table structure for table `accounts_tenant_charge`
--
CREATE TABLE IF NOT EXISTS `accounts_tenant_charge` (
`tenant_charge_id` int(11) NOT NULL AUTO_INCREMENT,
`tenant_charge_date` date DEFAULT NULL,
`tenant_charge_payment_terms` tinyint(4) DEFAULT NULL,
`tenant_charge_tenancy_id` int(11) DEFAULT NULL,
`tenant_charge_notes` text COLLATE utf8_bin,
`tenant_charge_total_amount` decimal(10,2) DEFAULT NULL,
`tenant_charge_date_created` date DEFAULT NULL,
`tenant_charge_date_updated` date DEFAULT NULL,
`tenant_charge_created_by` int(11) DEFAULT NULL,
`tenant_charge_updated_by` int(11) DEFAULT NULL,
PRIMARY KEY (`tenant_charge_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=17 ;
--
-- Dumping data for table `accounts_tenant_charge`
--
INSERT INTO `accounts_tenant_charge` (`tenant_charge_id`, `tenant_charge_date`, `tenant_charge_payment_terms`, `tenant_charge_tenancy_id`, `tenant_charge_notes`, `tenant_charge_total_amount`, `tenant_charge_date_created`, `tenant_charge_date_updated`, `tenant_charge_created_by`, `tenant_charge_updated_by`) VALUES
(15, '2015-06-22', 1, 25, '', '180.00', '2015-06-14', '2015-06-14', 1, 1),
(14, '2015-06-15', 1, 25, '', '550.00', '2015-06-14', '2015-06-14', 1, 1),
(16, '2015-06-27', 1, 25, '', '10.00', '2015-06-14', '2015-06-14', 1, 1);
Table #2 表#2
-- phpMyAdmin SQL Dump
-- version 4.0.10.7
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Jun 16, 2015 at 01:51 PM
-- Server version: 5.1.73-cll
-- PHP Version: 5.4.23
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
--
-- Database: `propsyst_atlas`
--
-- --------------------------------------------------------
--
-- Table structure for table `accounts_tenant_charge_payment`
--
CREATE TABLE IF NOT EXISTS `accounts_tenant_charge_payment` (
`tenant_charge_payment_id` int(11) NOT NULL AUTO_INCREMENT,
`tenant_charge_payment_date` date DEFAULT NULL,
`tenant_charge_payment_amount` decimal(10,2) DEFAULT NULL,
`tenant_charge_payment_method` tinyint(4) DEFAULT NULL,
`tenant_charge_payment_tenant_charge_id` int(11) DEFAULT NULL,
`tenant_charge_payment_notes` text COLLATE utf8_bin,
`tenant_charge_payment_date_created` date DEFAULT NULL,
`tenant_charge_payment_date_updated` date DEFAULT NULL,
`tenant_charge_payment_created_by` int(11) DEFAULT NULL,
`tenant_charge_payment_updated_by` int(11) DEFAULT NULL,
PRIMARY KEY (`tenant_charge_payment_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=12 ;
--
-- Dumping data for table `accounts_tenant_charge_payment`
--
INSERT INTO `accounts_tenant_charge_payment` (`tenant_charge_payment_id`, `tenant_charge_payment_date`, `tenant_charge_payment_amount`, `tenant_charge_payment_method`, `tenant_charge_payment_tenant_charge_id`, `tenant_charge_payment_notes`, `tenant_charge_payment_date_created`, `tenant_charge_payment_date_updated`, `tenant_charge_payment_created_by`, `tenant_charge_payment_updated_by`) VALUES
(9, '2015-06-15', '550.00', 2, 14, '', '2015-06-14', '2015-06-14', 1, 1),
(10, '2015-06-22', '50.00', 2, 15, '', '2015-06-16', '2015-06-16', 1, 1);
I guess the point is to get rid of several queries and two PHP loops. 我想重点是摆脱几个查询和两个PHP循环。 With no need for loops, this query the calculate the total charge, payments and balance in one SQL query:
无需循环,此查询在一个SQL查询中计算总费用,付款和余额:
SELECT
tenant_charge_tenancy_id,
sum(tenant_charge_total_amount) as charge,
IFNULL(sum(payments.payment), 0) as payment,
sum(tenant_charge_total_amount) - IFNULL(sum(payments.payment), 0) as balance
FROM accounts_tenant_charge
LEFT OUTER JOIN (
SELECT
tenant_charge_payment_tenant_charge_id,
sum(tenant_charge_payment_amount) as payment
FROM accounts_tenant_charge_payment
GROUP BY tenant_charge_payment_tenant_charge_id
) as payments ON tenant_charge_id = tenant_charge_payment_tenant_charge_id
WHERE tenant_charge_tenancy_id= 25
GROUP BY tenant_charge_tenancy_id
Instead of 25 you can put the $tenancy_details['tenancy_id']
in your PHP code. 您可以将
$tenancy_details['tenancy_id']
放在PHP代码中,而不是25。 By removing the WHERE tenant_charge_tenancy_id= ...
You can use this query to fetch all balances. 通过删除
WHERE tenant_charge_tenancy_id= ...
您可以使用此查询来获取所有余额。
you are querying rows from account_tenant_charge with an id, like this 你正在使用id来查询来自account_tenant_charge的行,就像这样
SELECT tenant_charge_id, tenant_charge_total_amount
FROM accounts_tenant_charge
WHERE tenant_charge_tenancy_id= ?
then for each row returned of that query, summing tenant_charge_total_amount in $tenant_balance
, and querying (with each tenant_charge_id
) 然后对于该查询返回的每一行,将
$tenant_balance
求和,并查询(使用每个tenant_charge_id
)
SELECT tenant_charge_payment_amount
FROM accounts_tenant_charge_payment
WHERE tenant_charge_payment_tenant_charge_id= ?
subtracting each tenant_charge_payment_amount from $tenant_balance
. 从
$tenant_balance
减去每个$tenant_balance
。
First of all, you can do that with a single query that will be compiled once and executed on the server to provide a single result, preventing issuing a lot of queries that must be compiled and executed sequentially and transferring data back and forth between the web server and the dbms just to calculate a single result. 首先,您可以使用单个查询来执行此操作,该查询将被编译一次并在服务器上执行以提供单个结果,从而防止发出大量必须按顺序编译和执行的查询以及在Web之间来回传输数据服务器和dbms只是为了计算单个结果。
It'd be something like this 它就是这样的
SELECT SUM(x.amount)
FROM (SELECT tenant_charge_total_amount amount
FROM accounts_tenant_charge
WHERE tenant_charge_tenancy_id= ?
UNION
SELECT -tenant_charge_payment_amount amount
FROM accounts_tenant_charge a
JOIN accounts_tenant_charge_payment b
ON tenant_charge_payment_tenant_charge_id = a.tenant_charge_id
WHERE tenant_charge_tenancy_id= ?) x
Second, use bind variables and prepared statements ( see here ). 其次,使用绑定变量和预处理语句( 参见此处 )。 If you build a dynamic query with each different id, you are forcing the db to compile the query each time, adding overhead to the execution time.
如果使用每个不同的id构建动态查询,则每次都强制db编译查询,从而增加执行时间的开销。 Also dynamic queries are susceptible to sql injection attacks.
动态查询也容易受到SQL注入攻击。
SQL is a declarative language, which means that you specify what you want to get and the dbms will execute it in the best way it finds. SQL是一个说明性语言,这意味着你指定你想要得到什么和DBMS会找到最好的方式执行。 You should first rely on the db engine to optimize your queries.
您应该首先依靠db引擎来优化查询。 Looping through resultsets to issue repeating queries will most probably prevent the engine from optimizing and will add a lot of preventable overhead.
循环遍历结果集以发出重复查询很可能会阻止引擎进行优化,并且会增加许多可预防的开销。
This query returns all tenant_charge_id
and lists the total amount that needs to be paid as well as the total amount that has been paid already. 此查询返回所有
tenant_charge_id
并列出需要支付的总金额以及已支付的总金额。
SELECT
charge.tenant_charge_id,
charge.tenant_charge_total_amount,
SUM(payment.tenant_charge_payment_amount) total_payments
FROM accounts_tenant_charge charge
LEFT JOIN accounts_tenant_charge_payment payment
ON payment.tenant_charge_payment_tenant_charge_id = charge.tenant_charge_id
GROUP BY tenant_charge_id
You could add a WHERE
clause to restrict the search for only a subset of tenants. 您可以添加
WHERE
子句以限制仅搜索租户的子集。
Try this: 试试这个:
SELECT tenant_charge_payment_tenant_charge_id, SUM(amount) balance
FROM (
SELECT tenant_charge_payment_tenant_charge_id, tenant_charge_total_amount amount
FROM accounts_tenant_charge
WHERE tenant_charge_tenancy_id='" . $tenancy_details['tenancy_id'] . "'
UNION
SELECT tenant_charge_payment_tenant_charge_id, CONCAT('-',tenant_charge_payment_amount) amount
FROM accounts_tenant_charge_payment
WHERE tenant_charge_payment_tenant_charge_id='" . $total_charge_amount_row['tenant_charge_id'] . "'
) temp
GROUP BY tenant_charge_payment_tenant_charge_id
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.