MySQL - 在相关子查询中限制聚合数据时嵌套子查询的替代方案

发布于 2024-12-29 17:57:02 字数 2308 浏览 1 评论 0原文

我有一个看起来像这样的表:

DataTable
+------------+------------+------------+
| Date       | DailyData1 | DailyData2 |
+------------+------------+------------+
| 2012-01-23 |     146.30 |     212.45 |
| 2012-01-20 |     554.62 |     539.11 |
| 2012-01-19 |     710.69 |     536.35 |
+------------+------------+------------+

我正在尝试创建一个视图(将其称为 AggregateView),该视图将为每个日期和每个数据列显示一些不同的聚合。例如,select * from AggregateView where Date = '2012-01-23' 可能会给出:

+------------+--------------+----------------+--------------+----------------+
| Date       | Data1_MTDAvg | Data1_20DayAvg | Data2_MTDAvg | Data2_20DayAvg |
+------------+--------------+----------------+--------------+----------------+
| 2012-01-23 |       697.71 |         566.34 |       601.37 |         192.13 |
+------------+--------------+----------------+--------------+----------------+

其中 Data1_MTDAvg 显示每个的 avg(DailyData1) 1 月 23 日之前的 1 月日期,Data1_20DayAvg 显示相同的结果,但针对表中的前 20 个日期。我不是 SQL 忍者,但我认为最好的方法是通过子查询。 MTD 平均值很简单:

select t1.Date, (select avg(t2.DailyData1) 
                 from DataTable t2 
                 where t2.Date <= t1.Date 
                   and month(t2.Date) = month(t1.Date) 
                   and year(t2.Date) = year(t1.Date)) Data1_MTDAvg 
from DataTable t1;

但由于需要限制返回结果的数量,我对 20 天平均值很感兴趣。请注意,表中的日期是不规则的,因此我不能使用日期间隔;我需要表中的最后 20 条记录,而不仅仅是过去 20 天的所有记录。我找到的唯一解决方案是使用嵌套子查询首先限制所选记录,然后取平均值。

子查询单独适用于单个硬编码日期:

select avg(t2.DailyData1) Data1_20DayAvg 
from (select DailyData1 
      from DataTable 
      where Date <= '2012-01-23' 
      order by Date desc 
      limit 0,20) t2;

但是尝试将其嵌入作为更大查询的一部分会失败:

select t1.Date, (select avg(t2.DailyData1) Data1_20DayAvg 
                 from (select DailyData1 
                       from DataTable 
                       where Date <= t1.Date 
                       order by Date desc 
                       limit 0,20) t2) 
from DataTable t1;
ERROR 1054 (42S22): Unknown column 't1.Date' in 'where clause'

通过搜索,我得到的印象是,您不能将相关子查询用作 from 子句,我认为这就是问题所在。另一个问题是我不确定 MySQL 是否会接受子查询中包含 from 子句的视图定义。有没有办法限制我的聚合选择中的数据而不诉诸子查询,以便解决这两个问题?

I have a table that looks something like this:

DataTable
+------------+------------+------------+
| Date       | DailyData1 | DailyData2 |
+------------+------------+------------+
| 2012-01-23 |     146.30 |     212.45 |
| 2012-01-20 |     554.62 |     539.11 |
| 2012-01-19 |     710.69 |     536.35 |
+------------+------------+------------+

I'm trying to create a view (call it AggregateView) that will, for each date and for each data column, show a few different aggregates. For example, select * from AggregateView where Date = '2012-01-23' might give:

+------------+--------------+----------------+--------------+----------------+
| Date       | Data1_MTDAvg | Data1_20DayAvg | Data2_MTDAvg | Data2_20DayAvg |
+------------+--------------+----------------+--------------+----------------+
| 2012-01-23 |       697.71 |         566.34 |       601.37 |         192.13 |
+------------+--------------+----------------+--------------+----------------+

where Data1_MTDAvg shows avg(DailyData1) for each date in January prior to Jan 23, and Data1_20DayAvg shows the same but for the prior 20 dates in the table. I'm no SQL ninja, but I was thinking that the best way to do this would be via subqueries. The MTD average is easy:

select t1.Date, (select avg(t2.DailyData1) 
                 from DataTable t2 
                 where t2.Date <= t1.Date 
                   and month(t2.Date) = month(t1.Date) 
                   and year(t2.Date) = year(t1.Date)) Data1_MTDAvg 
from DataTable t1;

But I'm getting hung up on the 20-day average due to the need to limit the number of results returned. Note that the dates in the table are irregular, so I can't use a date interval; I need the last twenty records in the table, rather than just all records over the last twenty days. The only solution I've found is to use a nested subquery to first limit the records selected, and then take the average.

Alone, the subquery works for individual hardcoded dates:

select avg(t2.DailyData1) Data1_20DayAvg 
from (select DailyData1 
      from DataTable 
      where Date <= '2012-01-23' 
      order by Date desc 
      limit 0,20) t2;

But trying to embed this as part of the greater query blows up:

select t1.Date, (select avg(t2.DailyData1) Data1_20DayAvg 
                 from (select DailyData1 
                       from DataTable 
                       where Date <= t1.Date 
                       order by Date desc 
                       limit 0,20) t2) 
from DataTable t1;
ERROR 1054 (42S22): Unknown column 't1.Date' in 'where clause'

From searching around I get the impression that you can't use correlated subqueries as part of a from clause, which I think is where the problem is here. The other issue is that I'm not sure if MySQL will accept a view definition containing a from clause in a subquery. Is there a way to limit the data in my aggregate selection without resorting to subqueries, in order to work around these two issues?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

草莓酥 2025-01-05 17:57:02

不可以,您不能在 FROM 子句中使用关联子查询。但您可以在 ON 条件中使用它们:

SELECT AVG(d.DailyData1) Data1_20DayAvg 
       --- other aggregate stuff on d (Datatable)
FROM 
      ( SELECT '2012-01-23' AS DateChecked
      ) AS dd
  JOIN
      DataTable AS d
    ON
      d.Date <= dd.DateChecked
    AND
      d.Date >= COALESCE( 
      ( SELECT DailyData1 
        FROM DataTable AS last20 
        WHERE Date <= dd.DateChecked 
          AND (other conditions for last20)
        ORDER BY Date DESC 
        LIMIT 1 OFFSET 19
      ), '1001-01-01'   )
WHERE (other conditions for d Datatable)

类似,对于许多日期:

SELECT dd.DateChecked 
     , AVG(d.DailyData1) Data1_20DayAvg 
       --- other aggregate stuff on d (Datatable)
FROM 
      ( SELECT DISTINCT Date AS DateChecked
        FROM DataTable 
      ) AS dd
  JOIN
      DataTable AS d
    ON
      d.Date <= dd.DateChecked
    AND
      d.Date >= COALESCE( 
      ( SELECT DailyData1 
        FROM DataTable AS last20
        WHERE Date <= dd.DateChecked 
          AND (other conditions for last20)
        ORDER BY Date DESC 
        LIMIT 1 OFFSET 19
      ), '1001-01-01'   )
WHERE (other conditions for d Datatable)
GROUP BY 
      dd.DateChecked 

两个查询都假设 Datatable.Date 具有 UNIQUE 约束。

No, you can't use correalted subqueries in the FROM clause. But you can use them in the ON conditions:

SELECT AVG(d.DailyData1) Data1_20DayAvg 
       --- other aggregate stuff on d (Datatable)
FROM 
      ( SELECT '2012-01-23' AS DateChecked
      ) AS dd
  JOIN
      DataTable AS d
    ON
      d.Date <= dd.DateChecked
    AND
      d.Date >= COALESCE( 
      ( SELECT DailyData1 
        FROM DataTable AS last20 
        WHERE Date <= dd.DateChecked 
          AND (other conditions for last20)
        ORDER BY Date DESC 
        LIMIT 1 OFFSET 19
      ), '1001-01-01'   )
WHERE (other conditions for d Datatable)

Similar, for many dates:

SELECT dd.DateChecked 
     , AVG(d.DailyData1) Data1_20DayAvg 
       --- other aggregate stuff on d (Datatable)
FROM 
      ( SELECT DISTINCT Date AS DateChecked
        FROM DataTable 
      ) AS dd
  JOIN
      DataTable AS d
    ON
      d.Date <= dd.DateChecked
    AND
      d.Date >= COALESCE( 
      ( SELECT DailyData1 
        FROM DataTable AS last20
        WHERE Date <= dd.DateChecked 
          AND (other conditions for last20)
        ORDER BY Date DESC 
        LIMIT 1 OFFSET 19
      ), '1001-01-01'   )
WHERE (other conditions for d Datatable)
GROUP BY 
      dd.DateChecked 

Both queries assume that Datatable.Date has a UNIQUE constraint.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文