Diskuze: Sloučení 15min hodnot do 1h
V předchozím kvízu, Online test znalostí SQL a databází, jsme si ověřili nabyté zkušenosti z kurzu.

Člen

Zobrazeno 50 zpráv z 54.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Online test znalostí SQL a databází, jsme si ověřili nabyté zkušenosti z kurzu.
Pokud je vzorec na separaci minut a dnu slozity, tak bys si udela pomocnou tabulku
tabulka: id [integer], date(rok-den-hodina-00) [datetime], minuta [integer]
Kde pro minutu=0 bych ubral hodina-1
Zgrupoval to podle minut.
A left joinem pridal vse o radku_id, co tam jeste chci mit.
A zapomnel jsi teda zminit, ze se jedna o MS-SQL. Ale treba se mi neco podari vygooglovat...
DATEPART(minute, t.čas) -- 0 az 59, to potrebujes na zjisteni, zda posunout o hodinu nebo ne (0 posouvas, jinak ne)
DateAdd(mi, -(DatePart(mi, t.čas))+15, t.čas) -- vytahnes minuty a odectes je a prictes 15 min, ziskas rrrr-mm-dd hh:15:ms.mms podle ktereho to muzes grupovat
Blbe je, kdyby tam ta 15 min hodnota nebyla, tak by tam bylo 15 min. Ale, to by slo taky resit. Pseudokod
m = DATEPART(minute, t.čas) -- vyberes minuty
if (m<15) {m=60+m} -- tem s nulou das max hodnotu
MIN(m) -- vyberes minimum pri grupovani
DateAdd(mi, -(DatePart(mi, t.čas))+m, t.čas) -- a pak to pridas do toho casu,
-- ze ktereho nasledne separujes, co potrebujes, na to asi ma msql nejakou funkci jako date fotmat nebo tak.
Asi nejak takhle
SELECT
CAST(Datum as date) as Datum,
CASE
WHEN DATEPART(minute,Datum) in (15,30,45) THEN DATEPART(hour,Datum) + 1
ELSE DATEPART(hour,Datum)
END as Hodina
, sum(t.kw) as kw
from [dbo].[Test5] t
group by
CAST(Datum as date),
CASE
WHEN DATEPART(minute,Datum) in (15,30,45) THEN DATEPART(hour,Datum) + 1
ELSE DATEPART(hour,Datum)
END
ORDER BY 1,2
no, jenze ten posun o hodinu muze znamenat i posun celeho dne, mesice ci roku, zmena hodnoty "date", v takovem pripade `GROUP BY CAST(Datum as date) ` bude grupovat chybne se starym datum. Ale chapu, proc to tam mas, pro urychleni grupovani. Proto bych sel do pomocne tabulky a nad ni az provadel grupovani.
Nemuze, datum se nemeni jenom cas muze byt 1 az 24 hodin
kdyz 0 az 23 tak misto case jenom DATEPART(hour,Datum) as Hodina
Uplne to nechapu. Uvedu priklad.
seskupit datum = 0:15+0:30+0:45+01:00
from
[dbo].[Test5] t
group by
CAST(Datum as date),
Tahle cast ti preci zgrupuje vse, co ma stejne datum rrrr-mm-dd.
A az nasledne to tridis pres CASE, ne?
Coz je sice super, ale zgrupuje to
2022-02-25 23:00
2022-02-25 23:15
2022-02-25 23:30
2022-02-25 23:45
jenze od chce sloucit
------ 2022-02-25 23:00 --------- nee
2022-02-25 23:15
2022-02-25 23:30
2022-02-25 23:45
2022-02-26 00:00
SELECT
CAST(Datum as date) as Datum,
CASE
WHEN DATEPART(minute,Čas) in (15,30,45,00) THEN DATEPART(hour,Čas) +1
ELSE DATEPART(hour,Datum)
END as Hodina
, sum(kW)/4 as kW
from [dbo].[Test_6]
group by
CAST(Datum as date),
CASE
WHEN DATEPART(minute,Čas) in (15,30,45,00) THEN DATEPART(hour,Čas) +1
ELSE DATEPART(hour,Datum)
END
ORDER BY
1 ASC, 2 ASC OFFSET 0 ROWS
Děkuji chlapci!
Tohle se už blíží k tomu co potřebuji, jen to opravdu počítá co píše
kolega Peter.
Hodnota po 15min je výkon, který jede vždy od začátku tj,
00-15,15-30,30-45,45-00 a aby byly hodnoty správně počítány, musí součet
čtvrthodin odpovídat celé hodině po.
Ještě tedy upřesním...
1.1.2020 00:15 + 00:15 + 00:30 + 00:30 + 00:45 + 01:00
Pak bude 1.1.2020 01:00 xxx kWh
(00:15 + 00:15 + 00:30 + 00:30 + 00:45 + 01:00)
Peteruv kód je pro mě dost složitý :-/
...
Ještě jsem si všimnul, že podle tohoto kódu přibyla nula, díky posunutí +1..Výsledek jsou dvě hodiny ve formátu 24 a 0 což také není žádoucí.
Tak malý zásah do úpravy kódu a vyřešeno
Velice Vám pánové děkuji!!
SELECT
CAST(Datum as date) as Datum,
CASE
WHEN DATEPART(minute,Čas) in (15,30,45) THEN DATEPART(hour,Čas) +1
ELSE DATEPART(hour,Čas)
END as Hodina
, sum(kW) as kW
from [dbo].[Test_6]
group by
CAST(Datum as date),
CASE
WHEN DATEPART(minute,Čas) in (15,30,45) THEN DATEPART(hour,Čas) +1
ELSE DATEPART(hour,Čas)
END
ORDER BY
1 ASC, 2 ASC OFFSET 0 ROWS
go
Konečný kód..
SELECT
CAST(Datum as date) as Datum,
CASE
WHEN DATEPART(minute,Čas) in (15,30,45) THEN DATEPART(hour,Čas) +1
ELSE DATEPART(hour,Čas)
END as Hodina
, sum(kW)/4 as kWh
from [dbo].[Test_6]
group by
CAST(Datum as date),
CASE
WHEN DATEPART(minute,Čas) in (15,30,45) THEN DATEPART(hour,Čas) +1
ELSE DATEPART(hour,Čas)
END
ORDER BY
1 ASC, 2 ASC OFFSET 0 ROWS
GO
Myslel jsem, že to zgrupnu po hodině, jenže po kontrole to nesedí
SELECT
Datum,
--CAST(Datum as date) as Datum,
DATEPART(minute, datum) as Minuta,
DateAdd(mi, -(DatePart(mi, datum)) +15, datum) as Datum_2
,sum(kW) as kW,
DATEPART(hour,datum) +1 as Hodina
from
[dbo].[Pomocná_Tab_Datum_3]
group by
Datum,
DATEPART(hour,datum)
--DateAdd(mi, -(DatePart(mi, datum))+15, datum)
ORDER BY
3 ASC, 4 ASC OFFSET 0 ROWS
GO
Proč jednoduše nevypisuješ záznamy, ve kterých čas odpovídá celé hodině, jako by to byly záznamy s časovým posunem -45 minut?
Časový posun o 45min chápu. Jak to ale, provést aby se suma, čtyř 15min po sobě jdoucí, spárovala s datumem následujícím netuším.
A proč by si je chtěl párovat? Každá hodnota je stavová a nesouvisí s žádnou jinou hodnotou. Proto vždy vypíšeš hodnotu v posledním časovém intervalu, pouze s upraveným časovým razítkem.
07.05.2020 15:15 0
07.05.2020 15:30 0
07.05.2020 15:45 0
07.05.2020 16:00 451
07.05.2020 16:15 474
07.05.2020 16:30 392
07.05.2020 16:45 474
07.05.2020 17:00 431
07.05.2020 17:15 385
Příklad..
Potřebuji hodnotu ze dne 07.05.2020 a hodiny 17:00
Výsledek musí být 1771
Pokud jsou to jednotlivé hodnoty stanovené vždy za 15 minut a nikoli stavové, situace je pak jiná. Způsobů je několik.
Vezmeš záznam, analyzuješ datum a čas, z toho určíš nový řetězec. Zjistíš, zda-li se v nové tabulce nenachází. Pokud ne, zapíšeš nový řetězec do tabulky spolu s hodnotou na daném záznamu. Pokud ano, přičteš hodnotu k danému záznamu druhé tabulky. Toto provedeš nad každým záznamem. Výsledkem jsou data v druhé tabulce.
Další způsob je že si zjistíš počet záznamů v tabulce. Na základě procházení konkrétního záznamu dokážeš určit, zda existuje grupa čtyř po sobě jdoucích dat patřících k sobě. Zapisovat budeš pouze záznamy s časovou hodnotou 15 minut a budeš aktualizovat hodnotu v druhé tabulce přičtením hodnot následujících tří záznamů (popř. zbylých). Ještě si musíš ošetřit prvních N záznamů, pokud nezačínají časovou hodnotou 15 minut.
Data jsou reálná a každým dnem přibývají. Teď řeším rok 2020,následně rok 2021. Časově razítko je tedy vždy celá hodina po 15min. Nevýhoda všeho je, že musí být součet čtyř 15min uvedený v následujícím datu a času.
Nevýhoda všeho je, že musí být součet čtyř 15min uvedený v následujícím datu a času.
To už jsou drobnosti.. Pošli prvních 10 a posledních 10 záznamů databáze, ať vidím její hranice a tím i adekvátní podobu předchozího a následujícího roku. To má vliv na prefixovou a postfixovou část algoritmu..
Rozumím tomu správně že:
07.05.2020 16:15 474
07.05.2020 16:30 392
07.05.2020 16:45 474
07.05.2020 17:00 431
Musí být uloženo jako: (je časové razítko správně?)
07.05.2020 17:15 1771
Takto by to mělo být
O7.05.2020 17:00 1771
Budu později u PC, tak pošlu.
Jsou to časové úseky měření po 15min kW, takže nikdy není konec.
Můžu si data rozdělit roky pokud by to pomohlo, ale kratší časový úsek není žádoucí.
To je docela podstatná věc to rozdělení, ať už z logického hlediska tak programátorského.
Pokud bychom měli následující úryvek databáze:
2022-01-01 00:00 0
2022-01-01 00:15 0
2022-01-01 00:30 0
...
pak z logického hlediska patří první řádek k předchozímu roku společně se třemi dalšími předchozími záznamy a neměl by tudíž mít visačku roku 2022.
Z programátorského hlediska je řešení celého problému podstatně jednoduší.
Takže rozhodně použij tuto formu:
2022-01-01 00:15 1
2022-01-01 00:30 2
2022-01-01 00:45 3
2022-01-01 00:00 4
...
2022-12-31 23:15 10
2022-12-31 23:30 20
2022-12-31 23:45 30
2022-12-31 00:00 40
Z toho chceme dostat toto:
2022-01-01 00:00 10
...
2022-12-31 00:00 100
Záznamy jsou chronologicky řazeny za sebou. Pokud existuje čtvrtý záznam, pak existující tři předchozí. Takže když vypisuješ nový záznam (každý čtvrtý), dokážeš určit hodnotu záznamu tvořící skupinu čtyř velice snadno.
Jak jednoduše a efektivně to celé můžeš zpracovat ukazují následující dva způsoby:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#define STRLEN 16
#define GROUPINDEX 4
struct sRecord {
char date[STRLEN + 1];
unsigned kW;
};
struct sRecord data[] = {
{"2022-01-01 00:15", 1},
{"2022-01-01 00:30", 3},
{"2022-01-01 00:45", 5},
{"2022-01-01 00:00", 7},
{"2022-12-31 23:15", 40},
{"2022-12-31 23:30", 30},
{"2022-12-31 23:45", 20},
{"2022-12-31 00:00", 10},
{"", 0}
};
int main(void) {
int recpos = 0;
while (strcmp(data[recpos].date, "")) {
if (!((recpos + 1) % GROUPINDEX)) {
for (int i = 1; i < GROUPINDEX; i++) data[recpos].kW += data[recpos - i].kW;
printf("%s\t%u\n", data[recpos].date, data[recpos].kW);
}
recpos++;
}
return 0;
}
Tento způsob vyžaduje ukončovací patici databáze v podobě prázdného řetězce. Postupně prochází záznamy jeden po druhém a ověřuje je na jejich platnost (provádí test na prázdný řetězec). Pro každý čtvrtý záznam stanoví novou hodnotu kW na základě hodnoty daného záznamu a tří předchozích a vypíše záznam. Tato verze není nejrychlejší (porovnání řetězců, procházení každého záznamu), jejím smyslem je ukázat jeden možný způsob.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#define STRLEN 16
#define GROUPINDEX 4
struct sRecord {
char date[STRLEN + 1];
unsigned kW;
};
struct sRecord data[] = {
{"2022-01-01 00:15", 1},
{"2022-01-01 00:30", 3},
{"2022-01-01 00:45", 5},
{"2022-01-01 00:00", 7},
{"2022-12-31 23:15", 40},
{"2022-12-31 23:30", 30},
{"2022-12-31 23:45", 20},
{"2022-12-31 00:00", 10}
};
int main(void) {
int reccount = sizeof data / sizeof(struct sRecord);
for (int i = GROUPINDEX - 1; i < reccount; i += GROUPINDEX) {
for (int j = 1; j < GROUPINDEX; j++) data[i].kW += data[i - j].kW;
printf("%s\t%u\n", data[i].date, data[i].kW);
}
return 0;
}
Tento způsob na rozdíl od předchozího způsobu nevyžaduje ukončovací
patici databáze, pouze samotné záznamy. Zjistí se počet záznamů v
databázi. Postupně prochází každý čtvrtý záznam, pro který stanoví
novou hodnotu kW na základě hodnoty daného záznamu a tří předchozích a
vypíše záznam. Tato verze je nejrychlejší možná. Z této verze se
můžeš inspirovat způsobem zápisu SQL dotazu, popř. se na dotazy úplně
vykašlat..
V obou verzích dojde k následovnému výpisu:
2022-01-01 00:00 16
2022-12-31 00:00 100
Jako vstupní data byly použity záznamy v následujícím formátu:
2022-01-01 00:15 1
2022-01-01 00:30 3
2022-01-01 00:45 5
2022-01-01 00:00 7
2022-12-31 23:15 40
2022-12-31 23:30 30
2022-12-31 23:45 20
2022-12-31 00:00 10
Kód psán v C, pokud něco není jasné, ptej se.
Tak asi tak
SELECT
CAST(Datum as date) as Datum,
CASE
WHEN DATEPART(minute,Datum) in (15,30,45) THEN DATEPART(hour,Datum) + 1
ELSE DATEPART(hour,Datum)
END as Hodina
, sum(t.kw) as kw
from (SELECT DATEADD(minute,45,Datum) as Datum,kw DROM [dbo].[Test5]) t
group by
CAST(Datum as date),
CASE
WHEN DATEPART(minute,Datum) in (15,30,45) THEN DATEPART(hour,Datum) + 1
ELSE DATEPART(hour,Datum)
END
ORDER BY 1,2
Tvůj kód funguje až na 0 a 24h.Stačilo by už hodinu 0 a 24 sečíst a
přiřadit časové razítko k hodině 24.
Dokážu si představit další dotaz na tuto tabulku. S tím datumem, ale
stále zápasím...:-/
pisu to jenom po pameti ale asi by podle toho stacili upravit CASE
CASE
WHEN DATEPART(minute,Datum) in (15,30,45) THEN DATEPART(hour,Datum) + 1
WHEN DATEPART(hour,Datum) = 0 THEN 24
ELSE DATEPART(hour,Datum)
END
Tak snad OK
Takto to sjednotí hodinu 0 a 24, což je super, ale součet je neznámí.
SELECT
CAST(Datum as date) as Datum,
CASE
WHEN DATEPART(minute,Datum) in (15,30,45) THEN DATEPART(hour,Datum) +1
WHEN DATEPART(hour,Datum) = 0 THEN 24
ELSE DATEPART(hour,Datum)
END as Hodina
, sum(kW) as kWh
from [dbo].[Pomocná_Tab_Datum_3]
group by
CAST(Datum as date),
CASE
WHEN DATEPART(minute,Datum) in (15,30,45) THEN DATEPART(hour,Datum) +1
WHEN DATEPART(hour,Datum) = 0 THEN 24
ELSE DATEPART(hour,Datum)
END
ORDER BY
1 ASC, 2 ASC OFFSET 0 ROWS
GO
Ale, proc to neudelate, jak jsem psal na zacatku? vytvorit si pomocny sloupec a grupovat to az dalsim selectem? Nebo to v mssql nejde? Nebo to tam pridat leftjoinem, jestli by to slo.
SELECT
a.id,
SUM(a.value),
b.datum
FROM
tabulka a
LEFT JOIN (
SELECT
a.id,
CASE
WHEN DATEPART(minute,a.Datum) in (15,30,45) THEN DATEPART(hour,a.Datum) + 1
ELSE DATEPART(hour,a.Datum)
END datum
) b
GROUP BY b.datum
---
SELECT
b.id,
SUM(b.value),
b.datum
FROM (
SELECT
a.id,
CASE
WHEN DATEPART(minute,a.Datum) in (15,30,45) THEN DATEPART(hour,a.Datum) + 1
ELSE DATEPART(hour,a.Datum)
END AS datum
a.value
FROM
tabulka a
) b
GROUP BY b.datum
Kdyz ten case pouzivate 2x, tak to preci musi byt pomale.
Na druhou stranu, grupovat to podle datumu tez neni uplne cool, pokud nad
sloupcem nemate index a pokud datum v prubehu zmenite, cimz v podstate prijde o
ten index.
Lepsi by bylo vytvorit si prave tu pomocnou tabulku s upravenym datumem,
indexy.
A jeste bych tam mozna pridal pri testovani kontrolu na pocet hodnot. COUNT(b.value), abys videl, ze pouzil 4 hodnoty.
Jinak, nespolehal bych na ten cas, ze bude vzdy 15 min. Treba se to trefi na
14.99. Takze bych tu podminku spis upravil na if-else.
A proc je tam pouzito WHEN DATEPART(minute,a.Datum) in (15,30,45)
,
kdyz by efektivnejsi bylo dat prvni podminku co nejkratsi?
WHEN DATEPART(minute,Datum) in (15,30,45) ... else ...
WHEN DATEPART(minute,Datum) in (0) ELSE ...
WHEN DATEPART(minute,Datum) < 15 ...
Ups, v tom sql dotazu je asi fura chyb. Uz ted vidim jednu. Tam se jedna jen
o tu cast datumu. Tak to zkusim znova
SELECT
b.id,
SUM(b.value),
b.datum2,
COUNT(b.value)
FROM (
SELECT
a.id,
a.value,
CASE
WHEN DatePart(minute, a.datum)>=15
THEN DateAdd(minute, - DatePart(minute, a.datum) + 15, a.datum)
ELSE DateAdd(minute, - DatePart(minute, a.datum) + 15 - 60, a.datum)
END AS datum2
FROM
tabulka a
) b
GROUP BY b.datum2
SELECT
CAST(Datum as date) as Datum,
DATEPART(hour,Datum) + 1 as Hodina,
sum(kW) as kWh
--,count(*),MIN(Datum),MAX(Datum)
from (SELECT DATEADD(minute,45,Datum) as Datum,kw FROM [dbo].[Test_kW]) X
group by
CAST(Datum as date),
DATEPART(hour,Datum)
ORDER BY
1 ASC, 2 ASC OFFSET 0 ROWS
snad OK
Když odeberu +1 , tak je vše OK.
Malá úprava a mám z toho kWh.
Děkuji za výbornou práci a pomoc!
SELECT
CAST(Datum as date) as Datum,
DATEPART(hour,Datum) as Hodina,
sum(kW)/4 as kWh
--,count(*),MIN(Datum),MAX(Datum)
from (SELECT DATEADD(minute,45,Datum) as Datum,kw FROM [dbo].[Pomocná_Tab_Datum_3]) X
group by
CAST(Datum as date),
DATEPART(hour,Datum)
ORDER BY
1 ASC, 2 ASC OFFSET 0 ROWS
Tvůj kód jsem samozřejmě také zkoušel, ale stále mi to házelo chyby s
kterými jsem se nedokázal poprat.
Děkuji za vynaložení energie na můj problém!
Chyby si musis hold odladit. S ms nedelam.
Ale, to Petanovo druhe reseni je takove elegantni. Pricte 45 min. Tim padem se
mu srovnaji spravne hodiny. A pak to grupuje podle dnu a hodin. Zgrupovani je
hotovo a jen treba pridat jeste spravne datum do vysledku. Coz by resil ten
CASE, kdybys ho tam mel.
2022-12-31 23:15 40
2022-12-31 23:30 30
2022-12-31 23:45 20
2022-12-31 00:00 10
Celou tuto serii ti posune na d+1, 2022-12-31+1, a protoze datum je tak
nevhodne zvolene, tak i m+1 a r+1
SELECT
CAST(Datum1 as date) as Datum,
DATEPART(hour, Datum1) as Hodina,
sum(kW)/4 as kWh
FROM (
SELECT
CASE
WHEN DatePart(minute, Datum)>=15
THEN DateAdd(minute, - DatePart(minute, Datum), Datum)
ELSE DateAdd(minute, - DatePart(minute, Datum) - 60, Datum)
END AS Datum1
kw
FROM
[dbo].[Pomocná_Tab_Datum_3]
) X
group by
CAST(Datum1 as date),
DATEPART(hour, Datum1)
ach jo, za END AS Datum1
ma byt carka
END AS Datum1,
protoze je tam seznam sloupcu
No, a slo by to teda i odcitanim
SELECT
CAST(DateAdd(hour, -1, Datum2) as date) as Datum,
DATEPART(hour, DateAdd(hour, -1, Datum2)) as Hodina,
sum(kW)/4 as kWh
FROM (
SELECT
DATEADD(minute,45,Datum) as Datum2,
kw
FROM
[dbo].[Pomocná_Tab_Datum_3]
) X
group by
CAST(Datum2 as date),
DATEPART(hour, Datum2)
Ty mas cely datum o hodinu posunuty. Takze musis pro vysledek posunout datum zpet. A pak teprve z men vytahovat DATE a part-HOUR.
Ajooo,máš pravdu...:-( už mě jebne z těch čísel a datumů ....:-D
c.bohumil - Dobre. To mas fuk, ktery pouzijes. Mas tu nekolik ruznych
zpusobu, ze kterych to snad poskladas, kdyz vis, jak to chces
Máš to přesně, správně znázorněno jak by to mělo být. Když si testneš data která jsem dal pro test, tak se tam v jednom případě posunou hodnoty o hodinu se správným datumem. V druhém případě hodnot správně, ale datum posunutý.
TO JAKO VAZNE PO 26.8.2020 23HOD MA NASLEDOVAT 26.8.2020 0HOD A POTOM 27.8.2020 1HOD ?
Jsou to časové úseky. 0-15,15-30,30-45,45-00.
15= zaměřena hodnota,
30=zaměřena hodnota,
45=zaměřena hodnota,
00=zaměřena hodnota(tahle hodnota hodiny jde k datumu kde začíná 15min
Hodnota je měřená každých 15min a k 15, 30,45,00 je naměřená hodnota. To
znamená, musí datum odpovídat vždy kdy začalo 15min.
A 23:15=00:00 je stejný datum.
No, asi trochu víc zmatený.. 😕
Zobrazeno 50 zpráv z 54.