尝试消除临时表 MYSQL 并尽可能将流程合并到一个语句中
Trying to eliminate Temporary Tables MYSQL and combine process into one statement if possible
我有一个应用程序正在跟踪 11 位销售员的销售情况。这是一个足够简单的过程,除了可以在销售员之间分享销售额,这会将他们的销售价值减半。这意味着如果两个销售员分担一份价值 100 美元的工作,那么每个销售员自己的销售价值仅为 50 美元。下面是我目前用来完成这个过程的代码,但它看起来很笨重,而且性能对我来说有点低迷。是否可以将其合并为一个进程并消除对临时 Table 的需要(我还看到在某个地方你不应该在生产中使用临时 Tables)
$sql = "DROP TEMPORARY TABLE IF EXISTS newbalancetbl" ;
mysqli_query ($db, $sql ) or ( "Error " . mysqli_error () ) ;
$newBalances = "
CREATE TEMPORARY TABLE newbalancetbl (
`custid` int NOT NULL,
`assigned` int NOT NULL,
`newBalance` double,
PRIMARY KEY(custid)
)
";
mysqli_query($db, $newBalances) or die ("Sql error : ".mysqli_error());
$year = date("Y");
$start = "01/01/".$year;
$today = date("Y-m-d");
$first = $year."-01-01";
$assignments = "SELECT leadid, price
FROM jobbooktbl
WHERE convertdate >= '".$first."' AND convertdate<='".$today."' AND (status=4 OR status=6 OR status=7 OR status=8 OR status=11)";
$assignmentsqry = mysqli_query($db,$assignments);
while ($row = mysqli_fetch_array($assignmentsqry)) {
$custid = $row["leadid"];
$price = $row["price"];
$statement = $db->prepare("INSERT INTO newbalancetbl (custid, newBalance) VALUES (?,?)");
$statement->bind_param('id', $custid, $price);
$statement->execute();
}
$sqlnewbal = "SELECT a.custid, COUNT(a.custid) AS assCnt
FROM assignmentstbl a, newbalancetbl b
WHERE a.custid=b.custid
GROUP BY a.custid";
$qrynewbal = mysqli_query($db,$sqlnewbal);
while ($row = mysqli_fetch_array($qrynewbal)) {
$custid = $row['custid'];
// $paid = $row["sumAmnt"];
$assigned = $row['assCnt'];
$usqlUpdate = $db->prepare("UPDATE newbalancetbl SET assigned=? WHERE custid=?");
$usqlUpdate->bind_param('ii',$assigned,$custid);
$usqlUpdate->execute();
}
$sqlnewbal = "SELECT *
FROM newbalancetbl";
$qrynewbal = mysqli_query($db,$sqlnewbal);
while ($row = mysqli_fetch_array($qrynewbal)) {
$custid = $row['custid'];
$assigned = $row['assigned'];
$newBalance = $row['newBalance'];
$newBal = $newBalance/$assigned;
$newBal - number_format($newBal,2);
$usqlUpdate = $db->prepare("UPDATE newbalancetbl SET newBalance=? WHERE custid=?");
$usqlUpdate->bind_param('di',$newBal,$custid);
$usqlUpdate->execute();
}
$salesArray = [];
$tesql = "SELECT SUM(n.newBalance) AS newB, u.username
FROM newbalancetbl n
INNER JOIN assignmentstbl a
ON a.custid=n.custid
INNER JOIN usertbl u
ON a.userid=u.userid
-- WHERE u.salesman=1
GROUP BY a.userid
ORDER BY newB DESC";
$teresult = mysqli_query($db,$tesql);
while ($row = mysqli_fetch_array($teresult)) {
$user = $row['username'];
$sales = $row['newB'];
array_push($salesArray, [$user,floatval($sales)]);
}
$arrayCount = count($salesArray);
$total_sales = 0;
$total_sales = array_sum( array_map(function($element){
return $element[1];
},
$salesArray));
$pretotal_sales = number_format($total_sales, 2);
$total_sales = '$' . number_format($total_sales, 2);
Table 架构:
jobbooktbl
作业stbl
这里只是一点刺
SELECT name,SUM(
SELECT (
SELECT price/
(SELECT COUNT(1) FROM jobs WHERE job.ID = Assignments.jobId)
FROM jobs WHERE
(SELECT COUNT(1) FROM assignment WHERE salesman.ID = assignment.salesmanId) > 0)
)
) AS commission
FROM salesman.
对所以select所有的推销员,从他所从事的所有工作中获取价格。将该价格除以工作人数。 table 可能已经更改了名称,但总的来说我认为这就是你想要的。
我将检查代码以了解它的作用,并在此过程中提出修改建议。
CREATE TEMPORARY TABLE newbalancetbl (
custid int NOT NULL,
assigned int NOT NULL,
newBalance double,
PRIMARY KEY(custid)
) ENGINE=Memory
好的。 table 看起来很小,因此您可能希望使用 ENGINE=Memory 使其更快。此外,请考虑使用 DECIMAL 类型而不是 DOUBLE。这不是强制性的,但可以避免舍入错误的麻烦。
无论如何。你的第一个查询。使用 php while() 循环填充 table 很慢而且没有必要。简单地做:
INSERT INTO newbalancetbl (custid, newBalance)
SELECT leadid, price
FROM jobbooktbl
WHERE convertdate BETWEEN '$first' AND '$today'
AND status IN (4,6,7,8,11);
注意 IN() 的使用,它更具可读性。此外,之间。 INSERT INTO SELECT 将比在 php 中循环查询结果快得多。另外,我在插入的列中没有看到 "assigned" 和 "newBalance",但是 table 没有指定任何默认值。您应该明确指定默认值。
现在,下一个查询是:
SELECT custid, COUNT(*) AS assCnt
FROM assignmentstbl a JOIN newbalancetbl b USING (custid)
GROUP BY custid
我用正确的语法更改了丑陋的旧 JOIN 语法(大约从 1999 年开始)。此外,COUNT(col) 计算 "col" 不为空的行数。所以 COUNT(a.custid) 这意味着 "a.custid" 实际上可以为空。因为它不能,所以这种语法很混乱。我将其替换为 count(*).
然后查看 PHP 中的结果,然后执行 "UPDATE newbalancetbl SET assigned=$assCnt WHERE custid=?"
您应该决定是使用名称 "assCnt" 还是 "assigned"。我更喜欢第一个,因为它是一个计数,名称中包含 "cnt" 可以减少混淆。现在,这个循环是不必要的,我们可以使用带有 JOIN 的单个 UPDATE,或者更好的是,从一开始就将值构建到 rable 中。因此,第一个查询变为:
INSERT INTO newbalancetbl (custid, newBalance, assigned)
SELECT j.leadid, j.price,
(SELECT count(*) FROM assignmentstbl a WHERE a.custid=j.leadid) AS assigned
FROM jobbooktbl j
WHERE j.convertdate BETWEEN '$first' AND '$today'
AND j.status IN (4,6,7,8,11);
我使用了子选择。请随意使用 JOIN。
下次查询。我将忽略“$newBal - number_format($newBal,2);”它什么都不做,因为你使用了“-”而不是“=”...这可以通过使用数字格式来解决,或者只使用这个:
UPDATE newbalancetbl
SET newBalance=ROUND(newBalance/assigned, 2)
消除了另一个 php 循环。但是我们可以消除更新,同时消除温度 table。
SELECT
j.leadid AS custid,
ROUND( j.price / (SELECT count(*) FROM assignmentstbl a WHERE a.custid=j.leadid), 2) AS newBalance
FROM jobbooktbl j
WHERE j.convertdate BETWEEN '$first' AND '$today'
AND j.status IN (4,6,7,8,11);
这应该给出与 temp table 包含的结果完全相同的结果,减去 "assigned" 列,该列无论如何都不会在其余代码中使用,因此我们可以删除它。现在,让我们将其插入到下一个查询中...
SELECT ROUND(SUM(n.newBalance), 2) AS newB, u.username
FROM (
SELECT
j.leadid AS custid,
j.price / (SELECT count(*) FROM assignmentstbl a WHERE a.custid=j.leadid) AS newBalance
FROM jobbooktbl j
WHERE j.convertdate BETWEEN '$first' AND '$today'
AND j.status IN (4,6,7,8,11)
) n
JOIN assignmentstbl a USING (custid)
JOIN usertbl u USING (userid)
-- WHERE u.salesman=1
GROUP BY a.userid
ORDER BY newB DESC
这应该可以满足您的要求。我在外部查询中移动了 ROUND。
假设总是有人被分配到该工作,并且问题中暗示了许多其他人,单个查询可以是:
SELECT SUM(n.newBalance) AS newB, u.username, u.userid
FROM
(SELECT
j.leadid as custid,
(j.price / COUNT(*)) as newBalance
FROM jobbooktbl j
INNER JOIN assignmentstbl a
ON a.custid = j.leadid
WHERE j.convertdate >= ?
AND j.convertdate <= ?
GROUP BY 1
) n
INNER JOIN assignmentstbl a
ON a.custid=n.custid
INNER JOIN usertbl u
ON a.userid=u.userid
GROUP BY u.userid
ORDER BY newB DESC;
n
子查询计算平均价格
If they are the only salesman on the job and the job was 0 dollars then they would be credited 0 (200/1=200) if they shared the job with another salesman, meaning there are two salesman assigned then they would only be credited with 0 (200/2=100)
假设如果有 4 个推销员被分配到同一份工作,他们每人将得到 50 美元。
我有一个应用程序正在跟踪 11 位销售员的销售情况。这是一个足够简单的过程,除了可以在销售员之间分享销售额,这会将他们的销售价值减半。这意味着如果两个销售员分担一份价值 100 美元的工作,那么每个销售员自己的销售价值仅为 50 美元。下面是我目前用来完成这个过程的代码,但它看起来很笨重,而且性能对我来说有点低迷。是否可以将其合并为一个进程并消除对临时 Table 的需要(我还看到在某个地方你不应该在生产中使用临时 Tables)
$sql = "DROP TEMPORARY TABLE IF EXISTS newbalancetbl" ;
mysqli_query ($db, $sql ) or ( "Error " . mysqli_error () ) ;
$newBalances = "
CREATE TEMPORARY TABLE newbalancetbl (
`custid` int NOT NULL,
`assigned` int NOT NULL,
`newBalance` double,
PRIMARY KEY(custid)
)
";
mysqli_query($db, $newBalances) or die ("Sql error : ".mysqli_error());
$year = date("Y");
$start = "01/01/".$year;
$today = date("Y-m-d");
$first = $year."-01-01";
$assignments = "SELECT leadid, price
FROM jobbooktbl
WHERE convertdate >= '".$first."' AND convertdate<='".$today."' AND (status=4 OR status=6 OR status=7 OR status=8 OR status=11)";
$assignmentsqry = mysqli_query($db,$assignments);
while ($row = mysqli_fetch_array($assignmentsqry)) {
$custid = $row["leadid"];
$price = $row["price"];
$statement = $db->prepare("INSERT INTO newbalancetbl (custid, newBalance) VALUES (?,?)");
$statement->bind_param('id', $custid, $price);
$statement->execute();
}
$sqlnewbal = "SELECT a.custid, COUNT(a.custid) AS assCnt
FROM assignmentstbl a, newbalancetbl b
WHERE a.custid=b.custid
GROUP BY a.custid";
$qrynewbal = mysqli_query($db,$sqlnewbal);
while ($row = mysqli_fetch_array($qrynewbal)) {
$custid = $row['custid'];
// $paid = $row["sumAmnt"];
$assigned = $row['assCnt'];
$usqlUpdate = $db->prepare("UPDATE newbalancetbl SET assigned=? WHERE custid=?");
$usqlUpdate->bind_param('ii',$assigned,$custid);
$usqlUpdate->execute();
}
$sqlnewbal = "SELECT *
FROM newbalancetbl";
$qrynewbal = mysqli_query($db,$sqlnewbal);
while ($row = mysqli_fetch_array($qrynewbal)) {
$custid = $row['custid'];
$assigned = $row['assigned'];
$newBalance = $row['newBalance'];
$newBal = $newBalance/$assigned;
$newBal - number_format($newBal,2);
$usqlUpdate = $db->prepare("UPDATE newbalancetbl SET newBalance=? WHERE custid=?");
$usqlUpdate->bind_param('di',$newBal,$custid);
$usqlUpdate->execute();
}
$salesArray = [];
$tesql = "SELECT SUM(n.newBalance) AS newB, u.username
FROM newbalancetbl n
INNER JOIN assignmentstbl a
ON a.custid=n.custid
INNER JOIN usertbl u
ON a.userid=u.userid
-- WHERE u.salesman=1
GROUP BY a.userid
ORDER BY newB DESC";
$teresult = mysqli_query($db,$tesql);
while ($row = mysqli_fetch_array($teresult)) {
$user = $row['username'];
$sales = $row['newB'];
array_push($salesArray, [$user,floatval($sales)]);
}
$arrayCount = count($salesArray);
$total_sales = 0;
$total_sales = array_sum( array_map(function($element){
return $element[1];
},
$salesArray));
$pretotal_sales = number_format($total_sales, 2);
$total_sales = '$' . number_format($total_sales, 2);
Table 架构:
jobbooktbl
作业stbl
这里只是一点刺
SELECT name,SUM(
SELECT (
SELECT price/
(SELECT COUNT(1) FROM jobs WHERE job.ID = Assignments.jobId)
FROM jobs WHERE
(SELECT COUNT(1) FROM assignment WHERE salesman.ID = assignment.salesmanId) > 0)
)
) AS commission
FROM salesman.
对所以select所有的推销员,从他所从事的所有工作中获取价格。将该价格除以工作人数。 table 可能已经更改了名称,但总的来说我认为这就是你想要的。
我将检查代码以了解它的作用,并在此过程中提出修改建议。
CREATE TEMPORARY TABLE newbalancetbl (
custid int NOT NULL,
assigned int NOT NULL,
newBalance double,
PRIMARY KEY(custid)
) ENGINE=Memory
好的。 table 看起来很小,因此您可能希望使用 ENGINE=Memory 使其更快。此外,请考虑使用 DECIMAL 类型而不是 DOUBLE。这不是强制性的,但可以避免舍入错误的麻烦。
无论如何。你的第一个查询。使用 php while() 循环填充 table 很慢而且没有必要。简单地做:
INSERT INTO newbalancetbl (custid, newBalance)
SELECT leadid, price
FROM jobbooktbl
WHERE convertdate BETWEEN '$first' AND '$today'
AND status IN (4,6,7,8,11);
注意 IN() 的使用,它更具可读性。此外,之间。 INSERT INTO SELECT 将比在 php 中循环查询结果快得多。另外,我在插入的列中没有看到 "assigned" 和 "newBalance",但是 table 没有指定任何默认值。您应该明确指定默认值。
现在,下一个查询是:
SELECT custid, COUNT(*) AS assCnt
FROM assignmentstbl a JOIN newbalancetbl b USING (custid)
GROUP BY custid
我用正确的语法更改了丑陋的旧 JOIN 语法(大约从 1999 年开始)。此外,COUNT(col) 计算 "col" 不为空的行数。所以 COUNT(a.custid) 这意味着 "a.custid" 实际上可以为空。因为它不能,所以这种语法很混乱。我将其替换为 count(*).
然后查看 PHP 中的结果,然后执行 "UPDATE newbalancetbl SET assigned=$assCnt WHERE custid=?"
您应该决定是使用名称 "assCnt" 还是 "assigned"。我更喜欢第一个,因为它是一个计数,名称中包含 "cnt" 可以减少混淆。现在,这个循环是不必要的,我们可以使用带有 JOIN 的单个 UPDATE,或者更好的是,从一开始就将值构建到 rable 中。因此,第一个查询变为:
INSERT INTO newbalancetbl (custid, newBalance, assigned)
SELECT j.leadid, j.price,
(SELECT count(*) FROM assignmentstbl a WHERE a.custid=j.leadid) AS assigned
FROM jobbooktbl j
WHERE j.convertdate BETWEEN '$first' AND '$today'
AND j.status IN (4,6,7,8,11);
我使用了子选择。请随意使用 JOIN。
下次查询。我将忽略“$newBal - number_format($newBal,2);”它什么都不做,因为你使用了“-”而不是“=”...这可以通过使用数字格式来解决,或者只使用这个:
UPDATE newbalancetbl
SET newBalance=ROUND(newBalance/assigned, 2)
消除了另一个 php 循环。但是我们可以消除更新,同时消除温度 table。
SELECT
j.leadid AS custid,
ROUND( j.price / (SELECT count(*) FROM assignmentstbl a WHERE a.custid=j.leadid), 2) AS newBalance
FROM jobbooktbl j
WHERE j.convertdate BETWEEN '$first' AND '$today'
AND j.status IN (4,6,7,8,11);
这应该给出与 temp table 包含的结果完全相同的结果,减去 "assigned" 列,该列无论如何都不会在其余代码中使用,因此我们可以删除它。现在,让我们将其插入到下一个查询中...
SELECT ROUND(SUM(n.newBalance), 2) AS newB, u.username
FROM (
SELECT
j.leadid AS custid,
j.price / (SELECT count(*) FROM assignmentstbl a WHERE a.custid=j.leadid) AS newBalance
FROM jobbooktbl j
WHERE j.convertdate BETWEEN '$first' AND '$today'
AND j.status IN (4,6,7,8,11)
) n
JOIN assignmentstbl a USING (custid)
JOIN usertbl u USING (userid)
-- WHERE u.salesman=1
GROUP BY a.userid
ORDER BY newB DESC
这应该可以满足您的要求。我在外部查询中移动了 ROUND。
假设总是有人被分配到该工作,并且问题中暗示了许多其他人,单个查询可以是:
SELECT SUM(n.newBalance) AS newB, u.username, u.userid
FROM
(SELECT
j.leadid as custid,
(j.price / COUNT(*)) as newBalance
FROM jobbooktbl j
INNER JOIN assignmentstbl a
ON a.custid = j.leadid
WHERE j.convertdate >= ?
AND j.convertdate <= ?
GROUP BY 1
) n
INNER JOIN assignmentstbl a
ON a.custid=n.custid
INNER JOIN usertbl u
ON a.userid=u.userid
GROUP BY u.userid
ORDER BY newB DESC;
n
子查询计算平均价格
If they are the only salesman on the job and the job was 0 dollars then they would be credited 0 (200/1=200) if they shared the job with another salesman, meaning there are two salesman assigned then they would only be credited with 0 (200/2=100)
假设如果有 4 个推销员被分配到同一份工作,他们每人将得到 50 美元。