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

标签 mysql

我有两个表:TableA和TableB。两者都有“日期”和“费率”字段。我想要最低价格的TableA及其日期;以及TableB的最大费率及其日期。另外,我喜欢列出每个月和每年的内容。

我使用下面的查询从一张表中获取最低和最高利率。但是我不知道如何从TableA中获得最低利率,而从TableB中获得最高利率。

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";

最佳答案

原始答案

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

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


从TableB中查询最大速率的类似查询是:

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


然后将此表达式组合到主查询的“最终”(下一个)版本中:

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引起了一些混乱。正如我在评论中的答复所指出的那样,问题是,如果表A和B中有1月和3月的数据,但由于某些特殊原因,则没有2月的数据,那么“最终”查询将不会显示2月的任何内容。如果要明确查看二月份的(缺少)值,请输入MonthYearTable
可以包含以下行:

Year    Month
2011    1
2011    2
2011    3


然后,您可以从那里选择要报告的月份,并在最终表中对极值查询进行LEFT OUTER JOIN。这样,即使TableA或TableB中没有2月(2011-02)的数据,也会在结果行中显示该数据。并且,假设您实际上从2009年1月到2012年12月每个月都有YearMonthTable中的数据,但是您希望报表涵盖从2009年7月到2011年6月的时期,则需要在MonthYearTable上指定过滤条件( d可能也会在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仅需要包含一个包含该类型值的单列。)

这样一来,故事就继续了……您可以无休止地处理查询,但是只要您逐步构建查询并系统地应用额外条件,就可以进行管理。临时执行此操作并尝试大爆炸查询(而不是系统地进行查询布置)只会导致混乱和灾难。



分析问题中的更新查询

选择列表中的相关子查询,尽管在主查询的FROM子句中的子查询的选择列表中;和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接受了它。实际上,这比我容易。我对查询的思考方式永远不会提出那种设计,因此基本上没有冒犯我这样编写查询的风险。这是否好是一个单独的讨论。

关于mysql - 来自不同mysql表的最小值和最大值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9441893/

相关文章:

php - 无法连接到数据库 (000webhost)

MySQL - 为什么在这种情况下二级索引不会减少此查询的时间?

PHP MySQL insert in float(3,1) 总是得到十进制的 "0"

mysql - 自动将 csv 文件传输到 MySQL

mysql - 同一字段中的双重搜索值

php - 根据时间间隔选择行

php - 图片上传到服务器,但表中没有创建字段

php - 通过移动应用程序为二维码的值添加前缀

php - 如何在node.js中全局声明数据库连接

php - 如何提高基于 Joomla 的 Intranet 系统的性能?