Mysql 选择组中的第一个和最后一个值

Mysql 选择组中的第一个和最后一个值,mysql,select,group-by,Mysql,Select,Group By,我有一个MySql表,其中包含每日股票报价(开盘、高盘、低盘、收盘和成交量),我正在尝试动态地将其转换为每周数据。到目前为止,我有以下功能,适用于高、低和音量: SELECT MIN(_low), MAX(_high), AVG(_volume), CONCAT(YEAR(_date), "-", WEEK(_date)) AS myweek FROM mystockdata GROUP BY myweek ORDER BY _date; 我需要在上面的查询中选择_open的第一个实例。因此,

我有一个MySql表,其中包含每日股票报价(开盘、高盘、低盘、收盘和成交量),我正在尝试动态地将其转换为每周数据。到目前为止,我有以下功能,适用于高、低和音量:

SELECT MIN(_low), MAX(_high), AVG(_volume),
CONCAT(YEAR(_date), "-", WEEK(_date)) AS myweek
FROM mystockdata
GROUP BY myweek
ORDER BY _date;
我需要在上面的查询中选择_open的第一个实例。因此,例如,如果周一(在特定的一周中)有一个假期,而股市周二开盘,那么开盘价应该从分组为周的周二中选择。同样,收盘价应该是该周的最后一次收盘价

是否可以在MySql中选择类似FIRST()和LAST()的内容,以便将上述内容封装在单个select中,而不是使用嵌套的select查询

下面是我的表的create语句,以了解模式:

delimiter $$
CREATE TABLE `mystockdata` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `symbol_id` int(11) NOT NULL,
  `_open` decimal(11,2) NOT NULL,
  `_high` decimal(11,2) NOT NULL,
  `_low` decimal(11,2) NOT NULL,
  `_close` decimal(11,2) NOT NULL,
  `_volume` bigint(20) NOT NULL,
  `add_date` date NOT NULL,
  PRIMARY KEY (`id`),
  KEY `Symbol_Id` (`symbol_id`,`add_date`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8$$

更新:没有空值,只要有假日/周末,表中就不会包含该日期的任何记录。

您可能需要使用
合并
函数来获取第一个值。但是,您需要确保那些没有数据的日子(周末和节假日)的
\u open
值为空

用途如下:

SELECT MIN(_low), MAX(_high), AVG(_volume), COALESCE(_open)
CONCAT(YEAR(_date), "-", WEEK(_date)) AS myweek
FROM mystockdata
GROUP BY myweek
ORDER BY _date;
对于last()值,我只能想到一个非常简单的解决方案,那就是使用
GROUP\u CONCAT
然后通过字符串操作从列表中获取最后一个值。所以也许是这样的:

SELECT MIN(_low), MAX(_high), AVG(_volume), COALESCE(_open), SUBSTRING_INDEX(GROUP_CONCAT(_close), ',', -1)
CONCAT(YEAR(_date), "-", WEEK(_date)) AS myweek
FROM mystockdata
GROUP BY myweek
ORDER BY _date;
注意:如果希望查询外观一致,也可以对第一项使用
GROUP\u CONCAT
方法,而不是合并

SELECT MIN(_low), MAX(_high), AVG(_volume), SUBSTRING_INDEX(GROUP_CONCAT(_open), ',', 1), SUBSTRING_INDEX(GROUP_CONCAT(_close), ',', -1)
CONCAT(YEAR(_date), "-", WEEK(_date)) AS myweek
FROM mystockdata
GROUP BY myweek
ORDER BY _date;

要使
GROUP\u CONCAT
正常工作,您还需要确保
\u open
\u close
字段中没有值的日期为空。

如果您使用的是MySQL 8,最好的解决方案是使用现在可用的窗口函数和/或。请看一看

但是如果您使用的是较旧版本的MySQL,那么这些函数就不是了 支持。你必须使用某种变通方法来模拟它们, 例如,您可以使用聚合字符串函数,创建一组按
\u date
\u open
和按
\u close
\u close
排序的一周的所有
\u open
\u close
值,并提取该组的第一个元素:

select
  min(_low),
  max(_high),
  avg(_volume),
  concat(year(_date), "-", lpad(week(_date), 2, '0')) AS myweek,
  substring_index(group_concat(cast(_open as CHAR) order by _date), ',', 1 ) as first_open,
  substring_index(group_concat(cast(_close as CHAR) order by _date desc), ',', 1 ) as last_close
from
  mystockdata
group by
  myweek
order by
  myweek
;
另一种解决方案是使用
SELECT
子句中带有
LIMIT 1
的子查询:

select
  min(_low),
  max(_high),
  avg(_volume),
  concat(year(_date), "-", lpad(week(_date), 2, '0')) AS myweek,
  (
    select _open
    from mystockdata m
    where concat(year(_date), "-", lpad(week(_date), 2, '0'))=myweek
    order by _date
    LIMIT 1
  ) as first_open,
  (
    select _close
    from mystockdata m
    where concat(year(_date), "-", lpad(week(_date), 2, '0'))=myweek
    order by _date desc
    LIMIT 1
  ) as last_close
from
  mystockdata
group by
  myweek
order by
  myweek
;
请注意,我在
myweek
中添加了字符串函数,以使周数始终为两位数,否则周数将无法正确排序


在将子字符串\u index与group\u concat()结合使用时也要小心:如果其中一个分组字符串包含逗号,则函数可能不会返回预期结果。

基本上,您需要做的是:

  • 按产品ID分组
  • 在每个组中,按位置排序
  • 选择按地点订购的同一产品的第一个价格
  • 将它们放在一起,可以使用以下查询:

    SELECT PRODUCTID, 
       SUBSTRING_INDEX(GROUP_CONCAT(CAST(LOCATION AS CHAR) ORDER BY LOCATION DESC), ',', 1) AS LOCATION,
       SUBSTRING_INDEX(GROUP_CONCAT(CAST(PRICE AS CHAR) ORDER BY LOCATION DESC), ',', 1) AS PRICE
    FROM ProductLocation
    GROUP BY PRODUCTID;
    

    请注意,MySQL没有用于GROUP BY的FIRST()和LAST()聚合函数,但可以使用GROUP_CONCAT()和SUBSTRING_INDEX()函数来模拟此类FIRST()和LAST()。

    从MySQL 8开始,您最好使用以下函数来完成任务:

    与
    t1 AS(
    选择低、高、量、浓度(年(\u日期)、“-”、周(\u日期))作为我的周
    来自mystockdata
    ),
    t2as(
    挑选
    t1.*,
    第一个打开的值(_open)(按myweek ORDER按_date划分)作为第一个打开的值,
    第一个_值(_close)(按myweek ORDER按_Datedesc划分)作为最后一个_close
    从t1开始
    )
    选择最小值(低)、最大值(高)、平均值(音量)、myweek、最小值(首次打开)、最大值(上次关闭)
    从t2开始
    按myweek分组
    按myweek订购;
    
    正如您所知,您描述的
    第一个
    最后一个
    在MySQL中不存在。只需一个查询就可以了,但这取决于您的表模式…更新您的帖子。谢谢,Jason。请看我上面的编辑。谢谢你,费希拉。第一种方法是使用
    GROUP\u CONCAT
    根据需要完美地工作!美丽的。这将我的执行时间从12分钟缩短到12秒。谢谢这个查询(像OP的查询一样,顺便说一句)在MySQL的严格模式打开(或者仅仅打开)时无法工作。
    \u date
    列在
    GROUP\u CONCAT
    ORDER BY
    子句以及
    SELECT
    查询的
    ORDER BY
    子句中都是未定义的。@LukasEder我已经更新了我的答案,因为更好的解决方案是使用MySQL8中现在可用的windows函数。感谢您的评论和支持answer@LukasEder谢谢,这肯定也是一个错误。我已经更新了我的答案(当MySQL8+不可用时,它可能仍然有用),我没有使用派生表,但它应该被修复。另外,最好在周数上使用lpad,以便对周进行正确排序。现在应该可以了:)OP的问题中不存在列/表