30

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

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

我需要在上述查询中选择 _open 的第一个实例。因此,例如,如果星期一(在特定的一周内)有假期并且股票市场在星期二开市,则 _open 值应该从分组到其周的星期二中选择。同样,收盘价应该是该周的最后一个 _close。

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

这是我的表的创建语句,以了解架构:

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$$

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

4

4 回答 4

50

如果您使用的是 MySQL 8,最好的解决方案是使用现在可用的窗口函数FIRST_VALUE()和/或LAST_VALUE() 。请看一下Lukas Eder 的回答

但如果您使用的是旧版本的 MySQL,则不支持这些功能。您必须使用某种变通方法来模拟它们,例如,您可以使用聚合字符串函数GROUP_CONCAT()创建一组按for和for排序的一周的所有值_open和值,并提取第一个元素放:_close_date_open_date desc_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
;

LIMIT 1另一种解决方案是在SELECT子句中使用子查询:

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
;

请注意,我将LPAD()字符串函数添加到myweek,以使周数始终为两位数,否则将无法正确排序周数。

将 substring_index 与 group_concat() 结合使用时也要小心:如果分组字符串之一包含逗号,则该函数可能不会返回预期结果。

于 2012-12-19T16:54:05.240 回答
8

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

WITH 
  t1 AS (
    SELECT _low, _high, _volume, CONCAT(YEAR(_date), "-", WEEK(_date)) AS myweek
    FROM mystockdata
  ),
  t2 AS (
    SELECT 
      t1.*, 
      FIRST_VALUE(_open) OVER (PARTITION BY myweek ORDER BY _date) AS first_open,
      FIRST_VALUE(_close) OVER (PARTITION BY myweek ORDER BY _date DESC) AS last_close
    FROM t1
  )
SELECT MIN(_low), MAX(_high), AVG(_volume), myweek, MIN(first_open), MAX(last_close)
FROM t2
GROUP BY myweek
ORDER BY myweek;
于 2018-09-19T19:00:45.143 回答
2

您可能需要发挥COALESCE作用才能获得第一个值。但是,您需要确保没有数据的日子(周末和节假日)_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() 值,我只能想到一个非常 hacky 的解决方案,即使用GROUP_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_CONCAT对第一项使用该方法而不是 coalesce

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_CONCAT正常工作,您还需要确保没有值的日期在_open_close字段中具有空值。

于 2012-12-19T16:51:25.690 回答
-1

基本上,你需要做的:

  1. 按 PRODUCTID 分组
  2. 在每个组内,按 LOCATION 排序
  3. 选择与 LOCATION 订购的相同产品的第一个价格

将它们放在一起,您可以使用以下查询:

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()。

于 2015-05-21T22:17:51.630 回答