J'ai un tableau MySql composé de cotations boursières quotidiennes (ouvertes, élevées, basses, fermées et en volume) que j'essaie de convertir en données hebdomadaires à la volée. Jusqu'à présent, j'ai la fonction suivante, qui fonctionne pour les hauts, les bas et le volume:
SELECT MIN(_low), MAX(_high), AVG(_volume),
CONCAT(YEAR(_date), "-", WEEK(_date)) AS myweek
FROM mystockdata
GROUP BY myweek
ORDER BY _date;
J'ai besoin de sélectionner la première instance de _open dans la requête ci-dessus. Ainsi, par exemple, s'il y a un jour férié le lundi (au cours d'une semaine) et que le marché boursier est ouvert le mardi, la valeur _open doit être sélectionnée à partir du mardi qui est regroupé dans sa semaine. De même, la valeur de fermeture doit être la dernière _close de cette semaine.
Est-il possible de sélectionner quelque chose comme FIRST () et LAST () dans MySql afin que ce qui précède puisse être enveloppé dans un seul SELECT plutôt que d'utiliser des requêtes de sélection imbriquées?
Voici l'instruction create de ma table pour avoir une idée du schéma:
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$$
Mise à jour: il n'y a pas de null, partout où il y a un jour férié/week-end, la table ne porte aucun enregistrement pour cette date.
Si vous utilisez MySQL 8, la solution préférable utiliserait les fonctions de fenêtre FIRST_VALUE () et/ou LAST_VALUE () , qui sont maintenant disponibles. Veuillez consulter réponse de Lukas Eder .
Mais si vous utilisez une ancienne version de MySQL, ces fonctions ne sont pas prises en charge. Vous devez les simuler en utilisant une sorte de solution de contournement, par exemple, vous pouvez utiliser la fonction de chaîne agrégée GROUP_CONCAT () qui crée un ensemble de tous les _open
et _close
valeurs de la semaine ordonnées par _date
pour _open
et par _date desc
pour _close
, et extraire le premier élément de l'ensemble:
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
;
Une autre solution consisterait à utiliser des sous-requêtes avec LIMIT 1
dans la clause 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
;
Veuillez noter que j'ai ajouté la fonction de chaîne LPAD () à myweek
, pour que le numéro de semaine soit toujours composé de deux chiffres, sinon les semaines ne seront pas commandées correctement.
Soyez également prudent lorsque vous utilisez substring_index en conjonction avec group_concat (): si l'une des chaînes groupées contient une virgule, la fonction peut ne pas retourner le résultat attendu.
À partir de MySQL 8, vous utiliseriez idéalement fonctions de fenêtre pour la tâche:
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;
Vous aurez probablement besoin de la fonction COALESCE
pour obtenir la première valeur. Cependant, vous devez vous assurer que les jours sans données (week-ends et jours fériés) ont une valeur nulle pour _open
ces jours-là sans données.
L'utilisation serait:
SELECT MIN(_low), MAX(_high), AVG(_volume), COALESCE(_open)
CONCAT(YEAR(_date), "-", WEEK(_date)) AS myweek
FROM mystockdata
GROUP BY myweek
ORDER BY _date;
Pour la dernière valeur (), je ne peux penser qu'à une solution assez hacky, qui serait d'utiliser GROUP_CONCAT
puis manipulation de chaîne pour obtenir la dernière valeur de la liste. Alors peut-être quelque chose comme ça:
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;
Notez que vous pouvez également utiliser le GROUP_CONCAT
approche pour le premier élément au lieu de fusionner si vous vouliez une recherche cohérente
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;
Pour GROUP_CONCAT
pour fonctionner correctement, vous devez également vous assurer que les dates sans valeur sont nulles dans _open
et _close
des champs.