select 一个 MySQL 查询中基于不同日期范围的平均值的语句
select statement for averages based on different date ranges in one MySQL query
基本上我正在尝试用这些数据制作图表。我可以将我的查询放入 PHP 中的 while 循环以获得每个平均值,但我更希望这是通过一个查询产生一个结果 table.
来完成的
<?php
date_default_timezone_set('America/Los_Angeles');
include('../connect.php');
$subcategory = 'T-Shirts';
$date = date('Y-m-d', strtotime('-29 days'));
$today = date("Y-m-d");
$subcategory = mysqli_real_escape_string($conp, $subcategory);
echo "<table border=\"1\">";
echo "<tr>";
echo "<th>date</th>";
echo "<th>average</th>";
echo "</tr>";
while (strtotime($date) <= strtotime($today)) {
$from_date = date ("Y-m-d", strtotime("-29 day", strtotime($date)));
$query = $conp->query("SELECT ROUND(SUM(OutCount)/30) AS 'average' FROM inventory
LEFT JOIN item
ON inventory.itemcode = item.itemcode
WHERE item.subcategory = '$subcategory'
AND TrDateTime BETWEEN '$from_date' AND '$date' AND transactiontype like 'OUT_%'");
if($query->num_rows){
while($row = mysqli_fetch_array($query, MYSQL_ASSOC)){
if(!empty($row['average'])){
$average = $row['average'];
}else{
$average = "N/A";
}
}
mysqli_free_result($query);
}else{
$average = "N/A";
}
$date = date ("Y-m-d", strtotime("+1 day", strtotime($date)));
echo "<tr>";
echo "<td>" . $date . "</td>";
echo "<td>" . $average . "</td>";
echo "</tr>";
}
echo "</table>";
?>
我得到了过去 30 天(包括今天)的所有日期以及该日期之前 29 天范围内的平均销售额。
+------------+----------+
| date | average |
+------------+----------+
| 2015-04-09 | 222 |
| 2015-04-10 | 225 |
| 2015-04-11 | 219 |
| ... | ... |
+------------+----------+
我可以通过这种方式获得所需的一切,但在这种情况下需要 运行 29 个查询,MySQL 会快得多。我开始想出一个 MySQL 过程,但我不确定当我尝试用 PHP.
调用它时它的效果如何
DELIMITER //
CREATE PROCEDURE average_daily_sales()
BEGIN
SET @today = CURDATE();
SET @date_var = CURDATE() - INTERVAL 29 DAY;
SET @from_date = @date_var - INTERVAL 29 DAY;
SET @to_date = @from_date + INTERVAL 29 DAY;
label1: WHILE @date_var < @today DO
SELECT DATE_FORMAT(trdatetime, '%Y-%m-%d') as 'date', ROUND(SUM(OutCount)/30) AS 'average'
FROM inventory
LEFT JOIN item
ON inventory.itemcode = item.itemcode
WHERE item.subcategory = 'T-Shirts'
AND trdatetime BETWEEN @from_date - INTERVAL 29 DAY AND @to_date
AND transactiontype like 'OUT_%';
SET @date_var = @date_var + INTERVAL 1 DAY;
END WHILE label1;
END; //
DELIMITER ;
最终,我更喜欢常规的 MySQL 语句,我可以用它一次性生成所需的结果 table。任何帮助将不胜感激。
如果您创建一个日历 table 并用一系列日期值填充它,例如
CREATE TABLE cal (dt DATE NOT NULL PRIMARY KEY) ;
INSERT INTO cal VALUES ('2015-04-01'),('2015-04-02'),('2015-04-03'), ... ;
您可以在这样的查询中将其用作行源:
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM cal
WHERE cal.dt >= '2015-04-01'
AND cal.dt < '2015-05-01'
ORDER BY cal.dt
创建 cal
日历 table 不是强制性的。我们可以使用一个内联视图并给它一个别名cal
。例如,在上面的查询中,我们可以替换这一行:
FROM cal
有了这个:
FROM ( SELECT DATE('2015-04-01') AS dt
UNION ALL SELECT DATE('2015-04-02')
UNION ALL SELECT DATE('2015-04-03')
UNION ALL SELECT DATE('2015-04-04')
UNION ALL SELECT DATE('2015-04-05')
) cal
或者,如果您有一个行源可以为您提供一系列连续的整数,从零开始,您可以从基准日期生成日期值,例如
FROM ( SELECT '2014-04-01' + INTERVAL i.n DAY
FROM source_of_integers i
WHERE i.n >= 0
AND i.n < 31
ORDER BY i.n
) cal
一些注意事项:
原查询显示的是外连接(LEFT
),但是WHERE子句中的等式谓词否定了连接的"outerness",等同于内连接。
查询中的某些列引用不合格。最佳做法是限定所有列引用,然后 reader 可以了解哪些列来自哪些 table,而不需要 reader 熟悉哪些列在哪些 table秒。当具有相同名称的列添加到查询中引用的另一个 table 时,这也可以防止语句在将来中断(出现 "ambiguous column" 错误)。)
跟进
就个人而言,对于有限数量的日期值,我会选择不引用 table 的内联视图。我会让 PHP 代码为我生成该查询。
有了开始日期,假设它是“2015-04-10”,我会获取该日期值并将其格式化为查询,相当于这样做:
$cal = "SELECT DATE('2015-04-10') AS dt" ;
然后我会旋转一个循环,并将该日期值增加 1 天。每次通过循环,我都会将下一个日期的 $cal
附加到 select,运行 通过循环三次的净效果相当于这样做:
$cal .= " UNION ALL SELECT DATE('2015-04-11')";
$cal .= " UNION ALL SELECT DATE('2015-04-12')";
$cal .= " UNION ALL SELECT DATE('2015-04-13')";
作为一个不太吸引人的替代方案,我们可以继续重复相同的开始日期值,并只增加一个整数值,然后让 MySQL 为我们计算日期。
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 1 DAY";
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 2 DAY";
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 3 DAY";
然后,我将 $cal
查询作为内联视图查询滑入 SQL 文本。像这样:
$sql = "SELECT cal.dt
, ( SELECT IFNULL(ROUND(SUM
,0) AS average_
FROM ( " . $cal . " ) cal
LEFT
JOIN item ON ... ";
无论如何,如果这是针对 有限的 个日期值(大约几十个),并且我只是想运行 偶尔会进行此查询,而不是针对每个请求重复使用此查询来攻击数据库服务器。)如果我要攻击服务器,我会创建并维护一个真实的 cal
table,而不是在每次查询时产生派生 table 的开销。
您是否有范围内每个不同日期的数据?如果是这样,这是一个稍微复杂的连接操作,但非常可行。
您可以通过以下方式获取您需要的日期范围:
SELECT DISTINCT
DATE(trdatetime)- INTERVAL 30 DAY AS startdate,
DATE(trdatetime) AS enddateplus1
FROM inventory
WHERE trdatetime >= NOW() - INTERVAL 31 DAY
调试此查询。查看以确保获得所需的每个日期范围。
然后您可以像这样将其加入您的业务查询
SELECT dates.startdate,
ROUND(SUM(OutCount)/30) AS 'average'
FROM (
SELECT DISTINCT
DATE(trdatetime)- INTERVAL 30 DAY AS startdate,
DATE(trdatetime) AS enddateplus1
FROM inventory
WHERE trdatetime >= NOW() - INTERVAL 31 DAY
) dates
LEFT JOIN inventory ON i.trdatetime >= dates.startdate
AND i.trdatetime < dates.enddateplus1
LEFT JOIN item ON i.itemcode = item.itemcode
WHERE item.subcategory = 'T-Shirts'
AND transactiontype like 'OUT_%'
GROUP BY dates.startdate
如果您的库存数据稀疏,也就是说,您没有所有日期的交易,那么您的日期查询将丢失一些行。
有一种方法可以填补那些缺失的行。但这在 s 中是一种痛苦。阅读本文以获取更多信息。 http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
请注意 BETWEEN
对于过滤 DATETIME
或 TIMESTAMP
值确实效果很差。
@OllieJones 和@spencer7593 的建议要么要求每天进行一次 'transaction' 以利用 SELECT DISTINCT DATE(trdatetime)
,要么需要创建另一个 table,要么需要生成派生的 table.
SELECT DISTINCT DATE(trdatetime)
对我来说不是一个选择,因为我没有每天 运行 的活动。
@spencer7593 建议的混合 PHP 和 MySQL 示例可以很好地生成派生的 table。最终静态版用了大概1.8秒就出结果了。问题是您需要额外的 PHP 来生成此...(请参阅@spencer7593 回答)
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM ( SELECT DATE('2015-04-01') AS dt
UNION ALL SELECT DATE('2015-04-02')
UNION ALL SELECT DATE('2015-04-03')
UNION ALL SELECT DATE('2015-04-04')
UNION ALL SELECT DATE('2015-04-05')
UNION ALL SELECT DATE('2015-04-06')
etc...
) cal
WHERE cal.dt >= '2015-04-01'
AND cal.dt < '2015-05-01'
ORDER BY cal.dt
我试图使用@spencer7593 的另一个答案。我按照他的建议用数字 0-31 创建了一个 "source of integers" table。这个方法用了1.8秒多一点。
SELECT cal.sd, cal.ed
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foobar'
AND n.TrDateTime >= cal.ed + INTERVAL -30 DAY
AND n.TrDateTime < cal.ed + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM ( SELECT (CURDATE() + INTERVAL -30 DAY) + INTERVAL i.n DAY as `ed`, (((CURDATE() + INTERVAL -30 DAY) + INTERVAL i.n DAY) + INTERVAL - 30 DAY) as `sd`
FROM source_of_integers i
WHERE i.n >= 0
AND i.n < 31
ORDER BY i.n
) cal
WHERE cal.ed >= CURDATE() + INTERVAL -29 DAY
AND cal.ed <= CURDATE()
ORDER BY cal.ed;
您需要这些日期的行源,实际上没有办法解决这个问题。最后我做了一个cal table..
CREATE TABLE cal (
dt DATE NOT NULL PRIMARY KEY
);
CREATE TABLE ints ( i tinyint );
INSERT INTO ints VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
INSERT INTO cal (dt)
SELECT DATE('2010-01-01') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
FROM ints a JOIN ints b JOIN ints c JOIN ints d JOIN ints e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) <= 3651
ORDER BY 1;
然后 运行 @spencer7593 的略微修改版本回答..
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM cal
WHERE cal.dt >= CURDATE() + INTERVAL -30 DAY
AND cal.dt < CURDATE()
ORDER BY cal.dt;
在我看来,我相信这是最干净(更少 PHP)和最高性能的答案。
以下是我如何索引库存 table 以大幅加快速度:
ALTER TABLE inventory ADD KEY (ItemCode, TrDateTime, TransactionType);
感谢@OllieJones 和@spencer7593 的帮助!
基本上我正在尝试用这些数据制作图表。我可以将我的查询放入 PHP 中的 while 循环以获得每个平均值,但我更希望这是通过一个查询产生一个结果 table.
来完成的<?php
date_default_timezone_set('America/Los_Angeles');
include('../connect.php');
$subcategory = 'T-Shirts';
$date = date('Y-m-d', strtotime('-29 days'));
$today = date("Y-m-d");
$subcategory = mysqli_real_escape_string($conp, $subcategory);
echo "<table border=\"1\">";
echo "<tr>";
echo "<th>date</th>";
echo "<th>average</th>";
echo "</tr>";
while (strtotime($date) <= strtotime($today)) {
$from_date = date ("Y-m-d", strtotime("-29 day", strtotime($date)));
$query = $conp->query("SELECT ROUND(SUM(OutCount)/30) AS 'average' FROM inventory
LEFT JOIN item
ON inventory.itemcode = item.itemcode
WHERE item.subcategory = '$subcategory'
AND TrDateTime BETWEEN '$from_date' AND '$date' AND transactiontype like 'OUT_%'");
if($query->num_rows){
while($row = mysqli_fetch_array($query, MYSQL_ASSOC)){
if(!empty($row['average'])){
$average = $row['average'];
}else{
$average = "N/A";
}
}
mysqli_free_result($query);
}else{
$average = "N/A";
}
$date = date ("Y-m-d", strtotime("+1 day", strtotime($date)));
echo "<tr>";
echo "<td>" . $date . "</td>";
echo "<td>" . $average . "</td>";
echo "</tr>";
}
echo "</table>";
?>
我得到了过去 30 天(包括今天)的所有日期以及该日期之前 29 天范围内的平均销售额。
+------------+----------+
| date | average |
+------------+----------+
| 2015-04-09 | 222 |
| 2015-04-10 | 225 |
| 2015-04-11 | 219 |
| ... | ... |
+------------+----------+
我可以通过这种方式获得所需的一切,但在这种情况下需要 运行 29 个查询,MySQL 会快得多。我开始想出一个 MySQL 过程,但我不确定当我尝试用 PHP.
调用它时它的效果如何DELIMITER //
CREATE PROCEDURE average_daily_sales()
BEGIN
SET @today = CURDATE();
SET @date_var = CURDATE() - INTERVAL 29 DAY;
SET @from_date = @date_var - INTERVAL 29 DAY;
SET @to_date = @from_date + INTERVAL 29 DAY;
label1: WHILE @date_var < @today DO
SELECT DATE_FORMAT(trdatetime, '%Y-%m-%d') as 'date', ROUND(SUM(OutCount)/30) AS 'average'
FROM inventory
LEFT JOIN item
ON inventory.itemcode = item.itemcode
WHERE item.subcategory = 'T-Shirts'
AND trdatetime BETWEEN @from_date - INTERVAL 29 DAY AND @to_date
AND transactiontype like 'OUT_%';
SET @date_var = @date_var + INTERVAL 1 DAY;
END WHILE label1;
END; //
DELIMITER ;
最终,我更喜欢常规的 MySQL 语句,我可以用它一次性生成所需的结果 table。任何帮助将不胜感激。
如果您创建一个日历 table 并用一系列日期值填充它,例如
CREATE TABLE cal (dt DATE NOT NULL PRIMARY KEY) ;
INSERT INTO cal VALUES ('2015-04-01'),('2015-04-02'),('2015-04-03'), ... ;
您可以在这样的查询中将其用作行源:
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM cal
WHERE cal.dt >= '2015-04-01'
AND cal.dt < '2015-05-01'
ORDER BY cal.dt
创建 cal
日历 table 不是强制性的。我们可以使用一个内联视图并给它一个别名cal
。例如,在上面的查询中,我们可以替换这一行:
FROM cal
有了这个:
FROM ( SELECT DATE('2015-04-01') AS dt
UNION ALL SELECT DATE('2015-04-02')
UNION ALL SELECT DATE('2015-04-03')
UNION ALL SELECT DATE('2015-04-04')
UNION ALL SELECT DATE('2015-04-05')
) cal
或者,如果您有一个行源可以为您提供一系列连续的整数,从零开始,您可以从基准日期生成日期值,例如
FROM ( SELECT '2014-04-01' + INTERVAL i.n DAY
FROM source_of_integers i
WHERE i.n >= 0
AND i.n < 31
ORDER BY i.n
) cal
一些注意事项:
原查询显示的是外连接(LEFT
),但是WHERE子句中的等式谓词否定了连接的"outerness",等同于内连接。
查询中的某些列引用不合格。最佳做法是限定所有列引用,然后 reader 可以了解哪些列来自哪些 table,而不需要 reader 熟悉哪些列在哪些 table秒。当具有相同名称的列添加到查询中引用的另一个 table 时,这也可以防止语句在将来中断(出现 "ambiguous column" 错误)。)
跟进
就个人而言,对于有限数量的日期值,我会选择不引用 table 的内联视图。我会让 PHP 代码为我生成该查询。
有了开始日期,假设它是“2015-04-10”,我会获取该日期值并将其格式化为查询,相当于这样做:
$cal = "SELECT DATE('2015-04-10') AS dt" ;
然后我会旋转一个循环,并将该日期值增加 1 天。每次通过循环,我都会将下一个日期的 $cal
附加到 select,运行 通过循环三次的净效果相当于这样做:
$cal .= " UNION ALL SELECT DATE('2015-04-11')";
$cal .= " UNION ALL SELECT DATE('2015-04-12')";
$cal .= " UNION ALL SELECT DATE('2015-04-13')";
作为一个不太吸引人的替代方案,我们可以继续重复相同的开始日期值,并只增加一个整数值,然后让 MySQL 为我们计算日期。
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 1 DAY";
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 2 DAY";
$cal .= " UNION ALL SELECT '2015-04-10' + INTERVAL 3 DAY";
然后,我将 $cal
查询作为内联视图查询滑入 SQL 文本。像这样:
$sql = "SELECT cal.dt
, ( SELECT IFNULL(ROUND(SUM
,0) AS average_
FROM ( " . $cal . " ) cal
LEFT
JOIN item ON ... ";
无论如何,如果这是针对 有限的 个日期值(大约几十个),并且我只是想运行 偶尔会进行此查询,而不是针对每个请求重复使用此查询来攻击数据库服务器。)如果我要攻击服务器,我会创建并维护一个真实的 cal
table,而不是在每次查询时产生派生 table 的开销。
您是否有范围内每个不同日期的数据?如果是这样,这是一个稍微复杂的连接操作,但非常可行。
您可以通过以下方式获取您需要的日期范围:
SELECT DISTINCT
DATE(trdatetime)- INTERVAL 30 DAY AS startdate,
DATE(trdatetime) AS enddateplus1
FROM inventory
WHERE trdatetime >= NOW() - INTERVAL 31 DAY
调试此查询。查看以确保获得所需的每个日期范围。
然后您可以像这样将其加入您的业务查询
SELECT dates.startdate,
ROUND(SUM(OutCount)/30) AS 'average'
FROM (
SELECT DISTINCT
DATE(trdatetime)- INTERVAL 30 DAY AS startdate,
DATE(trdatetime) AS enddateplus1
FROM inventory
WHERE trdatetime >= NOW() - INTERVAL 31 DAY
) dates
LEFT JOIN inventory ON i.trdatetime >= dates.startdate
AND i.trdatetime < dates.enddateplus1
LEFT JOIN item ON i.itemcode = item.itemcode
WHERE item.subcategory = 'T-Shirts'
AND transactiontype like 'OUT_%'
GROUP BY dates.startdate
如果您的库存数据稀疏,也就是说,您没有所有日期的交易,那么您的日期查询将丢失一些行。
有一种方法可以填补那些缺失的行。但这在 s 中是一种痛苦。阅读本文以获取更多信息。 http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
请注意 BETWEEN
对于过滤 DATETIME
或 TIMESTAMP
值确实效果很差。
@OllieJones 和@spencer7593 的建议要么要求每天进行一次 'transaction' 以利用 SELECT DISTINCT DATE(trdatetime)
,要么需要创建另一个 table,要么需要生成派生的 table.
SELECT DISTINCT DATE(trdatetime)
对我来说不是一个选择,因为我没有每天 运行 的活动。
@spencer7593 建议的混合 PHP 和 MySQL 示例可以很好地生成派生的 table。最终静态版用了大概1.8秒就出结果了。问题是您需要额外的 PHP 来生成此...(请参阅@spencer7593 回答)
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM ( SELECT DATE('2015-04-01') AS dt
UNION ALL SELECT DATE('2015-04-02')
UNION ALL SELECT DATE('2015-04-03')
UNION ALL SELECT DATE('2015-04-04')
UNION ALL SELECT DATE('2015-04-05')
UNION ALL SELECT DATE('2015-04-06')
etc...
) cal
WHERE cal.dt >= '2015-04-01'
AND cal.dt < '2015-05-01'
ORDER BY cal.dt
我试图使用@spencer7593 的另一个答案。我按照他的建议用数字 0-31 创建了一个 "source of integers" table。这个方法用了1.8秒多一点。
SELECT cal.sd, cal.ed
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foobar'
AND n.TrDateTime >= cal.ed + INTERVAL -30 DAY
AND n.TrDateTime < cal.ed + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM ( SELECT (CURDATE() + INTERVAL -30 DAY) + INTERVAL i.n DAY as `ed`, (((CURDATE() + INTERVAL -30 DAY) + INTERVAL i.n DAY) + INTERVAL - 30 DAY) as `sd`
FROM source_of_integers i
WHERE i.n >= 0
AND i.n < 31
ORDER BY i.n
) cal
WHERE cal.ed >= CURDATE() + INTERVAL -29 DAY
AND cal.ed <= CURDATE()
ORDER BY cal.ed;
您需要这些日期的行源,实际上没有办法解决这个问题。最后我做了一个cal table..
CREATE TABLE cal (
dt DATE NOT NULL PRIMARY KEY
);
CREATE TABLE ints ( i tinyint );
INSERT INTO ints VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
INSERT INTO cal (dt)
SELECT DATE('2010-01-01') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
FROM ints a JOIN ints b JOIN ints c JOIN ints d JOIN ints e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) <= 3651
ORDER BY 1;
然后 运行 @spencer7593 的略微修改版本回答..
SELECT cal.dt
, ( -- correlated subquery references value returned from cal
SELECT ROUND(SUM(n.OutCount)/30)
FROM inventory n
JOIN item t
ON t.itemcode = n.itemcode
WHERE t.subcategory = 'foo'
AND n.TrDateTime >= cal.dt + INTERVAL -28 DAY
AND n.TrDateTime < cal.dt + INTERVAL 1 DAY
AND n.transactiontype LIKE 'OUT_%'
) AS `average`
FROM cal
WHERE cal.dt >= CURDATE() + INTERVAL -30 DAY
AND cal.dt < CURDATE()
ORDER BY cal.dt;
在我看来,我相信这是最干净(更少 PHP)和最高性能的答案。
以下是我如何索引库存 table 以大幅加快速度:
ALTER TABLE inventory ADD KEY (ItemCode, TrDateTime, TransactionType);
感谢@OllieJones 和@spencer7593 的帮助!