毎日の株価情報(始値、高値、安値、終値、出来高)で構成される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内でラップできるようにすることは可能ですか?
スキーマを理解するためのテーブルの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$$
更新:nullはありません。休日/週末がある場合は、テーブルにはその日付のレコードは含まれません。
MySQL 8を使用している場合、望ましいソリューションはウィンドウ関数 FIRST_VALUE() および/または を使用しますLAST_VALUE() 、現在利用可能です。 ルーカス・エダーの答え をご覧ください。
ただし、古いバージョンのMySQLを使用している場合、これらの機能はサポートされていません。何らかの回避策を使用してそれらをシミュレートする必要があります。たとえば、すべての_open
のセットを作成する集約文字列関数 GROUP_CONCAT() を使用できます。 _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
;
別のソリューションでは、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
;
週番号を常に2桁にするために、 LPAD() 文字列関数をmyweek
に追加したことに注意してください。正しく注文してください。
また、group_concat()とともにsubstring_indexを使用する場合は注意してください。グループ化された文字列の1つにカンマが含まれていると、関数は期待される結果を返さない場合があります。
MySQL 8以降では、タスクに理想的には window functions を使用します。
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;
最初の値を取得するには、おそらくCOALESCE
関数が必要になります。ただし、データのない日(週末と祝日)には、データのない日に_open
のnull値が設定されていることを確認する必要があります。
使用法は次のとおりです。
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_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
アプローチを使用することもできます。
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
フィールドの値のない日付にnullが含まれていることも確認する必要があります。