简体   繁体   English

如何使用PHP和MySQL从两个有条件链接的表中有效地SUM和SUBTRACT

[英]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. 循环遍历结果集以发出重复查询很可能会阻止引擎进行优化,并且会增加许多可预防的开销。

See DEMO here . 在这里查看DEMO

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.

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