Vous demandez trois choses, qui peuvent être considérées comme trois étapes. Obtenir la fenêtre de temps est assez simple, et seulement légèrement compliqué par votre colonne étant un timestamp plutôt qu'une date. Vous avez laissé entendre que cela fonctionnera à l'heure, mais il est possible que ce soit un peu plus tard - peut-être une seconde ou deux? - Il est donc probablement plus sûr de le prendre en compte. Vous pouvez utiliser the trunc()
function pour modifier une valeur de date à la précision requise, afin de ne regarder que l'heure actuelle que vous tronqueriez à HH [24]. Vous pouvez alors cast revenir à un horodatage. Et vous pouvez utiliser interval arithmetic pour trouver l'heure avant que:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF3';
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF3 TZR';
select systimestamp,
trunc(systimestamp, 'HH24') as a,
cast(trunc(systimestamp, 'HH24') as timestamp) as b,
cast(trunc(systimestamp, 'HH24') as timestamp) - interval '1' hour as c
from dual;
SYSTIMESTAMP A B C
------------------------------ ------------------- ----------------------- -----------------------
2017-03-01 09:25:39.342 +00:00 2017-03-01 09:00:00 2017-03-01 09:00:00.000 2017-03-01 08:00:00.000
Les commandes sont alter session
juste pour contrôler la façon dont les différents types de données sont affichés, à titre de comparaison. (Ne comptez pas sur les paramètres NLS dans votre code réel, utilisez to_char()
pour la mise en forme finale des valeurs datetime en tant que chaînes).
Notez que le résultat de la troncature est maintenant une date (A dans cette sortie), donc je l'ai renvoyé à un horodatage (B). La gamme que vous voulez est essentiellement time >= A and time < B
. Et vous pouvez utiliser sysdate
au lieu de systimestamp
comme entrée à trunc()
.
Pour vos données d'exemple en utilisant systimestamp
ou sysdate
ne va rien trouver, donc je vais utiliser un faux temps fixe pour le reste, généré dans un CTE pour la séparation. Où j'ai utilisé now
du CTE, vous utiliseriez systimestamp ou sysdate.
La deuxième partie consiste à obtenir la moyenne de chaque nom au cours de cette période. Qui est simple agrégation:
with fake_time(now) as (
select timestamp '2017-02-10 13:01:07' from dual
)
select name,
avg(value) as avg_value,
cast(trunc(now, 'HH24') as timestamp) as time
from fake_time
join table1 on time >= cast(trunc(now, 'HH24') as timestamp) - interval '1' hour
and time < cast(trunc(now, 'HH24') as timestamp)
group by name, now;
NAME AVG_VALUE TIME
------- ---------- -----------------------
QWER1_Z 20 2017-02-10 13:00:00.000
QWER1_V 35 2017-02-10 13:00:00.000
TEST1_Z 15 2017-02-10 13:00:00.000
TEST1_V 10 2017-02-10 13:00:00.000
Pour ramasser les lignes que vous voulez que j'ai fait le temps faux 13:00 au lieu de 12:00. La moyenne que vous avez montrée pour TEST1_V
était également fausse.
L'étape suivante est de faire pivoter ceux-ci dans le format souhaité, en une seule ligne. Pour cela, vous pouvez ajouter la racine (c.-à-d.TEST1
ou QWER1
) et la lettre (Z ou V) sous forme de colonnes supplémentaires dans le jeu de résultats, puis de les utiliser comme sous-requête pour la pivot operation - cela nécessite 11g ou plus:
with fake_time(now) as (
select timestamp '2017-02-10 13:01:07' from dual
)
select z_name, z_value, v_name, v_value, time
from (
select substr(name, 1, length(name) - 2) as root,
substr(name, -1) as zv,
name,
avg(value) as avg_value,
cast(trunc(now, 'HH24') as timestamp) as time
from fake_time
join table1 on time >= cast(trunc(now, 'HH24') as timestamp) - interval '1' hour
and time < cast(trunc(now, 'HH24') as timestamp)
group by substr(name, 1, length(name) - 2), name, now
)
pivot (max(name) as name, max(avg_value) as value for (zv) in ('Z' as z, 'V' as v));
Z_NAME Z_VALUE V_NAME V_VALUE TIME
------- ---------- ------- ---------- -----------------------
TEST1_Z 15 TEST1_V 10 2017-02-10 13:00:00.000
QWER1_Z 20 QWER1_V 35 2017-02-10 13:00:00.000
Il peut être une autre étape nécessaire ; Dans votre exemple de sortie, vous avez inclus une liste des valeurs d'origine qui ont été moyennées, mais vous n'avez pas confirmé si vous en vouliez réellement ou si elles montraient simplement comment la moyenne était calculée pour nous aider à comprendre ce que vous deviez faire. Si vous voulez vraiment comprendre que vous pouvez utiliser listagg()
et concaténation pour construire la chaîne « moyenne » avant pivotante:
'avg(' || listagg(value, ',') within group (order by value) || ') = ' || avg(value)
as avg_value,
pour obtenir
Z_NAME Z_VALUE V_NAME V_VALUE TIME
------- -------------------- ------- -------------------- -----------------------
TEST1_Z avg(10,20) = 15 TEST1_V avg(10) = 10 2017-02-10 13:00:00.000
QWER1_Z avg(20) = 20 QWER1_V avg(30,40) = 35 2017-02-10 13:00:00.000
Comme je l'ai dit plus tôt, je n'ai utilisé le fake_date
CTE pour obtenir une date qui correspond à vos données d'échantillon. Votre vraie requête sera plus comme:
select z_name, z_value, v_name, v_value, time
from (
select substr(name, 1, length(name) - 2) as root,
substr(name, -1) as zv,
name,
avg(value) as avg_value,
cast(trunc(sysdate, 'HH24') as timestamp) as time
from table1
where time >= cast(trunc(sysdate, 'HH24') as timestamp) - interval '1' hour
and time < cast(trunc(sysdate, 'HH24') as timestamp)
group by substr(name, 1, length(name) - 2), name
)
pivot (max(name) as name, max(avg_value) as value for (zv) in ('Z' as z, 'V' as v));
Vérifiez 'GROUP BY'! – jarlh
Vous voulez réellement afficher la liste des valeurs moyennées, ou simplement la moyenne? La partie de plage de temps est plutôt droite; l'agrégation n'est que légèrement compliquée si vous voulez aussi la liste. (De plus, [envoyez du texte plutôt que des images] (http://meta.stackoverflow.com/a/285557/266304), et les données source comme DDL/DML seraient utiles). –
BTW, votre requête s'exécute-t-elle réellement?!? – jarlh