来自不同mysql表的最小值和最大值

时间:2012-02-25 06:33:38

标签: mysql

我有两个表:TableA和TableB。两者都有“日期”和“费率”字段。我希望得到TableA及其日期的最低费率;表B的最高费率及其日期。此外,我喜欢列出每个月和每年。

我使用下面的查询从一个表中获取最低和最高费率。但我无法弄清楚如何从TableB获得TableA最高费率的最低费率。

SELECT
MIN(rate) AS minRate,
(SELECT date FROM TableA WHERE rate = min(t2.rate) and  month(date) = month(t2.date) and     year(date) = year(t2.date) limit 1 ) as minDate,
MONTHNAME(date) as MN, YEAR(date) as YN,
MAX(rate) AS maxRate,
(SELECT date FROM TableAs  WHERE rate = max(t2.rate) and  month(date) = month(t2.date)  and year(date) = year(t2.date) limit 1) as maxDate
FROM TableA  t2
GROUP BY YEAR(date) , MONTH(date)";

编辑1:我最终得到了这个。

SELECT a.MinYear AS Year, a.MinMonth AS Month, a.MinRate, b.MaxRate, a.MinDate, b.MaxDate
FROM (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate, 
    (SELECT date FROM $TableA  WHERE rate = MIN(t2.rate) AND YEAR(date) =    YEAR(t2.date) AND  MONTH(date) = MONTH(t2.date) limit 1) AS MinDate
        FROM $TableA t2
        GROUP BY MinYear, MinMonth
   ) AS a   
JOIN (SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate, 
    (SELECT date FROM $TableB  WHERE rate = MAX(t3.rate) AND YEAR(date) = YEAR(t3.date) AND  MONTH(date) = MONTH(t3.date) limit 1) AS MaxDate
        FROM $TableB t3
        GROUP BY MaxYear, MaxMonth
   ) AS b
ON a.MinYear = b.MaxYear AND a.MinMonth = b.MaxMonth
ORDER BY Year, Month

编辑2 Jonathan Leffler的查询(测试后稍有变化)表现更好:

SELECT a.MinYear AS Year, a.MinMonth AS Month, a.MinDate, a.MinRate, b.MaxDate, b.MaxRate

FROM (SELECT n.MinYear, n.MinMonth, a.Date AS MinDate, n.MinRate
      FROM $TableA AS a
      JOIN (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate
             FROM $TableA
             GROUP BY MinYear, MinMonth
           ) AS n
        ON a.Rate = n.MinRate AND YEAR(a.Date) = n.MinYear AND MONTH(a.Date) = n.MinMonth
   ) AS a

 JOIN (SELECT x.MaxYear, x.MaxMonth, b.Date AS MaxDate, x.MaxRate
      FROM $TableB AS b

        JOIN (SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate
             FROM $TableB
             GROUP BY MaxYear, MaxMonth
           ) AS x
        ON b.Rate = x.MaxRate AND YEAR(b.Date) = x.MaxYear AND MONTH(b.Date) = x.MaxMonth
   ) AS b
ON a.MinYear = b.MaxYear AND a.MinMonth = b.MaxMonth
ORDER BY Year, Month";

3 个答案:

答案 0 :(得分:2)

原始答案

您需要创建两个结果集,一个来自tableA,一个来自TableB,然后加入它们。与任何复杂的SQL查询一样,我将结果部分构建。首先,我们需要TableA的每月最低费率:

SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate
  FROM TableA
 GROUP BY MinYear, MinMonth;

表B中最大费率的类似查询是:

SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate
  FROM TableB
 GROUP BY MaxYear, MaxMonth;

现在您需要在年和月列上加入这两个结果:

SELECT a.MinYear AS Year, a.MinMonth AS Month, a.MinRate, b.MaxRate
  FROM (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate
          FROM TableA
         GROUP BY MinYear, MinMonth
       ) AS a
  JOIN (SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate
          FROM TableB
         GROUP BY MaxYear, MaxMonth
       ) AS b
    ON a.MinYear = b.MaxYear AND a.MinMonth = b.MaxMonth
 ORDER BY Year, Month;

管理缺失数据的扩展

如果你不得不担心从TableA或TableB中丢失数据,那么生活就会复杂一些。然后你真的需要一个FULL OUTER JOIN,但有些DBMS不提供。如果您不得不担心两个表中几个月都没有代表,那么您需要生成一个表格,指定您感兴趣的日期(月份和年份),然后您可以左键输出每个表格。上面两个表达式。

SELECT c.RefYear AS Year, c.RefMonth AS Month, a.MinRate, b.MaxRate
  FROM MonthYearTable AS c
  LEFT JOIN
       (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate
          FROM TableA
         GROUP BY MinYear, MinMonth
       ) AS a
    ON c.RefYear = a.MinYear AND c.RefMonth = a.MinMonth
  LEFT JOIN
       (SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate
          FROM TableB
         GROUP BY MaxYear, MaxMonth
       ) AS b
    ON c.RefYear = b.MaxYear AND c.RefMonth = b.MaxMonth
 ORDER BY Year, Month;

如果需要,您可以从MonthYearTable指定您感兴趣的日期范围。


查找极值发生率的日期

如果如评论中所建议的那样,答案应该包括每月最大或最低费率发生时的确切日期,那么“找到极值”。子查询更复杂:

SELECT n.MinYear, n.MinMonth, a.Date AS MinDate, n.MinRate
  FROM TableA AS a
  JOIN (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate
          FROM TableA
         GROUP BY MinYear, MinMonth
       ) AS n
    ON a.Rate = n.MinRate AND YEAR(a.Date) = n.MinYear AND MONTH(a.Date) = n.MinMonth

对于针对TableB的查询类似:

SELECT x.MaxYear, x.MaxMonth, b.Date AS MaxDate, x.MaxRate
  FROM TableB AS b
  JOIN (SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate
          FROM TableB
         GROUP BY MaxYear, MaxMonth
       ) AS x
    ON b.Rate = x.MinRate AND YEAR(b.Date) = x.MaxYear AND MONTH(b.Date) = x.MaxMonth

将这些结合起来导致查询:

SELECT a.MinYear AS Year, a.MinMonth AS Month, a.MinDate, a.MinRate, b.MinDate, b.MaxRate
  FROM (SELECT n.MinYear, n.MinMonth, a.Date AS MinDate, n.MinRate
          FROM TableA AS a
          JOIN (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate
                  FROM TableA
                 GROUP BY MinYear, MinMonth
               ) AS n
            ON a.Rate = n.MinRate AND YEAR(a.Date) = n.MinYear AND MONTH(a.Date) = n.MinMonth
       ) AS a
  JOIN (SELECT x.MaxYear, x.MaxMonth, b.Date AS MaxDate, x.MaxRate
          FROM TableB AS b
          JOIN (SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate
                  FROM TableB
                 GROUP BY MaxYear, MaxMonth
               ) AS x
            ON b.Rate = x.MinRate AND YEAR(b.Date) = x.MaxYear AND MONTH(b.Date) = x.MaxMonth
       ) AS a
    ON a.MinYear = b.MaxYear AND a.MinMonth = b.MaxMonth
 ORDER BY Year, Month;

请注意,如果在给定月份的三个不同日期报告相同的最低费率,则该月份将有三行产出,每个产出一天。实际上,如果还有两天发生最大费率,则该月的六个输出行。如果这不是所需要的,那么你可以在一个月内的日期做一个合适的聚合(最有可能是MIN或MAX):

SELECT n.MinYear, n.MinMonth, MAX(a.Date) AS MinDate, n.MinRate
  FROM TableA AS a
  JOIN (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate
          FROM TableA
         GROUP BY MinYear, MinMonth
       ) AS n
    ON a.Rate = n.MinRate AND YEAR(a.Date) = n.MinYear AND MONTH(a.Date) = n.MinMonth
 GROUP BY n.MinYear, n.MinMonth, n.MinRate

然后将此表达式合并到“#final”中。 (下一个)主要查询的版本:

SELECT a.MinYear AS Year, a.MinMonth AS Month, a.MinDate, a.MinRate, b.MinDate, b.MaxRate
  FROM (SELECT n.MinYear, n.MinMonth, MAX(a.Date) AS MinDate, n.MinRate
          FROM TableA AS a
          JOIN (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate
                  FROM TableA
                 GROUP BY MinYear, MinMonth
               ) AS n
            ON a.Rate = n.MinRate AND YEAR(a.Date) = n.MinYear AND MONTH(a.Date) = n.MinMonth
         GROUP BY x.MaxYear, x.MaxMonth, x.MaxRate
       ) AS a
  JOIN (SELECT x.MaxYear, x.MaxMonth, MAX(b.Date) AS MaxDate, x.MaxRate
          FROM TableB AS b
          JOIN (SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate
                  FROM TableB
                 GROUP BY MaxYear, MaxMonth
               ) AS x
            ON b.Rate = x.MinRate AND YEAR(b.Date) = x.MaxYear AND MONTH(b.Date) = x.MaxMonth
         GROUP BY n.MinYear, n.MinMonth, n.MinRate
       ) AS a
    ON a.MinYear = b.MaxYear AND a.MinMonth = b.MaxMonth
 ORDER BY Year, Month;

我讨厌尝试一次性写出最后的查询。但是通过分阶段构建它,即使没有将其提交给DBMS,我仍然有点自信,它接近准确。如果我正在测试它,我可能直接进行最终查询,但是如果它有问题,那么我测试组件查询,一次处理一个子查询,直到部件生成正确的结果,然后结合总查询。


扩展以处理日期范围和再次丢失数据

在评论中,MonthYearTable引起了轻微的混淆。正如我在评论中的回答中所指出的那样,问题在于,如果您在1月和3月的表A和表B中有数据,但由于某些特殊原因,2月没有数据,那么最后的数据是'查询不会显示2月的任何内容。如果要显式查看2月的(缺少)值,则为MonthYearTable 可以包含如下行:

Year    Month
2011    1
2011    2
2011    3

您可以从那里选择要报告的月份,并使用最终表格中的极值查询进行LEFT OUTER JOIN。这样,即使2月(2011-02)的TableA或TableB中没有数据,也会显示结果行。并且,假设您实际上在2009年1月到2012年12月的每个月都有YearMonthTable中的数据,但是您希望报告涵盖2009年7月到2011年6月期间,您需要在MonthYearTable上指定过滤条件(和您可能也会在TableA和TableB上执行此操作,因为优化器不太可能为您推断子范围。)

SELECT c.RefYear AS Year, c.RefMonth AS Month, a.MinDate, a.MinRate, b.MaxDate, b.MaxRate
  FROM MonthYearTable AS c
  LEFT JOIN
       (SELECT n.MinYear, n.MinMonth, MAX(a.Date) AS MinDate, n.MinRate
          FROM TableA AS a
          JOIN (SELECT YEAR(m.date) AS MinYear, MONTH(m.date) AS MinMonth, MIN(m.rate) AS MinRate
                  FROM TableA AS m
                 WHERE m.date BETWEEN DATE '2009-07-01' AND DATE '2011-06-30'
                 GROUP BY MinYear, MinMonth
               ) AS n
            ON a.Rate = n.MinRate AND YEAR(a.Date) = n.MinYear AND MONTH(a.Date) = n.MinMonth
         GROUP BY x.MaxYear, x.MaxMonth, x.MaxRate
       ) AS a
  JOIN (SELECT x.MaxYear, x.MaxMonth, MAX(b.Date) AS MaxDate, x.MaxRate
          FROM TableB AS b
          JOIN (SELECT YEAR(m.date) AS MaxYear, MONTH(m.date) AS MaxMonth, MAX(m.rate) AS MaxRate
                  FROM TableB AS m
                 WHERE m.date BETWEEN DATE '2009-07-01' AND DATE '2011-06-30'
                 GROUP BY MaxYear, MaxMonth
               ) AS x
            ON b.Rate = x.MinRate AND YEAR(b.Date) = x.MaxYear AND MONTH(b.Date) = x.MaxMonth
         GROUP BY n.MinYear, n.MinMonth, n.MinRate
       ) AS a
    ON a.MinYear = b.MaxYear AND a.MinMonth = b.MaxMonth
 WHERE ((c.RefYear = 2009 AND c.RefMonth >= 7) OR (c.RefYear > 2009))
   AND ((c.RefYear = 2011 AND c.RefMonth <= 6) OR (c.RefYear < 2011))
 ORDER BY Year, Month;

您可以对查询应用更多调整,尤其是在更多位置添加日期范围过滤器。您可以考虑使用如下表达式:

WHERE (c.RefYear * 100 + c.RefMonth) BETWEEN 200907 AND 201106

表示MonthYearTable中的日期范围。 (为此,Informix支持的DATETIME YEAR TO MONTH类型是理想的; MonthYearTable只需要包含一个包含该类型值的列。)

所以故事还在继续......你可以无休止地使用查询,但只要你将它构建成碎片并系统地应用额外的标准,你就能够进行管理。执行 ad hoc 并尝试进行大爆炸查询(而不是系统地进行查询)只会导致混乱和灾难。


分析问题中的更新查询

select-list中的相关子查询,尽管在主查询的FROM子句中的子查询的select-list中;和LIMIT条款也是如此。哎哟!我倾向于避免在可能的情况下在选择列表中编写子查询;他们伤害了我的大脑,甚至超过了我写的风格。 OTOH,经过精心处理,他们有时会做必要的工作。

以我的风格重新格式化时,修改后的查询如下所示:

SELECT a.MinYear AS Year, a.MinMonth AS Month, a.MinRate, b.MaxRate, a.MinDate, b.MaxDate
  FROM (SELECT YEAR(date) AS MinYear, MONTH(date) AS MinMonth, MIN(rate) AS MinRate, 
               (SELECT date
                  FROM $TableA
                 WHERE rate = MIN(t2.rate)
                   AND YEAR(date) = YEAR(t2.date) AND MONTH(date) = MONTH(t2.date)
                 LIMIT 1
               ) AS MinDate
          FROM $TableA t2
         GROUP BY MinYear, MinMonth
        ) AS a   
  JOIN (SELECT YEAR(date) AS MaxYear, MONTH(date) AS MaxMonth, MAX(rate) AS MaxRate, 
               (SELECT date
                  FROM $TableB
                 WHERE rate = MAX(t3.rate)
                   AND YEAR(date) = YEAR(t3.date) AND MONTH(date) = MONTH(t3.date)
                  LIMIT 1
               ) AS MaxDate
          FROM $TableB t3
         GROUP BY MaxYear, MaxMonth
        ) AS b
     ON a.MinYear = b.MaxYear AND a.MinMonth = b.MaxMonth
  ORDER BY Year, Month;

这可能有用,但我不打算对此进行教诲。我会说,我熟悉的大多数DBMS可能会对MAX(t3.rate)MIN(t2.rate)条款产生影响。没有实验,我不相信查询。我也倾向于不相信LIMIT 1,更不用说当没有订购标准时。如果可以应用LIMIT的多行,那么DBMS会随心所欲地返回哪一行,并且非确定性查询通常是一个坏主意。

所以,虽然这可能有用,但它并不是我用过的东西 - 即使假设我的DBMS接受了它。实际上,对我来说比这更容易;我对查询的思考方式永远不会出现在那个设计中,所以基本上没有我像这样制定查询的风险。这个好的与否是一个单独的讨论。

答案 1 :(得分:0)

为什么不能在select语句中使用TableB

答案 2 :(得分:0)

我不确定我是否完全了解您的需求,但我认为您需要每年每月的表A中的最低费率以及每年每月的表B的最高费率。

因此,假设每个tableA和tableB有2列“rate”和“date”,以下查询应该有效:

每年每月从tableA检索最小值:

SELECT DATE_FORMAT(`date`, '%Y%m') AS `yearmonth`, MIN(rate) AS `minrate`
FROM `tableA`
GROUP BY `yearmonth`;

每年每月从tableB检索最大值:

SELECT DATE_FORMAT(`date`, '%Y%m') AS `yearmonth`, MAX(rate) AS `maxrate`
FROM `tableB`
GROUP BY `yearmonth`;

希望它有所帮助!