I have this SQL statement to retrieve ONLY the last day of stock quotes. But I am missing the data if the data falls on a non-end of month data.
This is my SQL.
select symbol, trade_date, close_price from soa_pse_stock_quotes_history where symbol = '2GO'
group by symbol, trade_date, close_price
having max(trade_date::date) = (date_trunc('month', trade_date::date) + interval '1 month - 1 day')::date
order by trade_date desc limit 100 ;
I am missing quotes for July and Apr because Jul data is July 29, the last business day of the month. The same for April.
What is the correct SQL to fetch the last quote for each end of the month?
Result:
2GO 2022-08-31 00:00:00 7.28
2GO 2022-06-30 00:00:00 6.82
2GO 2022-05-31 00:00:00 7.1
2GO 2022-03-31 00:00:00 7.31
2GO 2022-02-28 00:00:00 7.5
2GO 2021-12-31 00:00:00 7.61
2GO 2021-09-30 00:00:00 8.14
2GO 2021-08-31 00:00:00 8.06
2GO 2021-06-30 00:00:00 8.48
2GO 2021-05-31 00:00:00 8.34
2GO 2021-04-30 00:00:00 8.4
2GO 2021-03-31 00:00:00 8.5
2GO 2020-09-30 00:00:00 8.42
2GO 2020-06-30 00:00:00 9.63
Create a subquery that finds the max date for a symbol by month using GROUP BY symbol, date_trunc('month', trade_date)). Then join this to the table to get the closing price.
CREATE TABLE stock_table (
symbol varchar,
close_price numeric,
trade_date date
);
INSERT INTO stock_table
VALUES ('2GO', 7.0, '02/26/2022'),
('2GO', 7.1, '02/28/2022'),
('2GO', 7.4, '03/31/2022'),
('2GO', 7.2, '04/28/2022'),
('2GO', 7.3, '04/29/2022');
SELECT
st.symbol,
st.trade_date,
st.close_price
FROM
stock_table AS st
JOIN (
SELECT
symbol,
max(trade_date) AS max_date
FROM
stock_table
WHERE
symbol = '2GO'
GROUP BY
symbol,
date_trunc('month', trade_date)) AS m_date
ON st.symbol = m_date.symbol
AND st.trade_date = m_date.max_date;
symbol | trade_date | close_price
--------+------------+-------------
2GO | 02/28/2022 | 7.1
2GO | 03/31/2022 | 7.4
2GO | 04/29/2022 | 7.3
Related
In PostgreSQL 11, I am trying to get a weekend time range. From 17:00 Friday to Sunday 17:00.
So far I am able to get a working day by doing
select * from generate_series(date '2021-01-01',date '2021-12-31',interval '1' day) as t(dt) where extract (dow from dt) between 1 and 5;
However, I am have trouble creating 2 columns from start (17:00 Friday) to finish (17:00 Sunday).
Expected output should be something like this:
start stop
2022-10-07 17:00 2022-10-09 17:00
2022-10-14 17:00 2022-10-16 17:00
2022-10-21 17:00 2022-10-23 17:00
To get a series of all hours between 17:00 on Friday and 17:00 on Sunday.
SELECT
*
FROM
generate_series(timestamp '2021-01-01', timestamp '2021-12-31', interval '1' hour) AS t (dt)
WHERE
extract(dow FROM dt) IN (5, 6, 0)
AND CASE WHEN extract(dow FROM dt) = 5 THEN
extract(hour FROM dt) >= 17
WHEN extract(dow FROM dt) = 0 THEN
extract(hour FROM dt) <= 17
ELSE
extract(hour FROM dt) IS NOT NULL
END;
UPDATE
Get two timestamps that represent start and stop of each period Friday 17:00 to Sunday 17:00 over a range of dates.
SELECT
dt + '17:00'::time as start, (dt + '17:00'::time) + '2 days'::interval as stop
FROM
generate_series(date '2022-01-01', date '2022-12-31', interval '1' day) AS t (dt)
WHERE
extract(dow FROM dt) = 5
;
start | stop
-------------------------+-------------------------
01/07/2022 17:00:00 PST | 01/09/2022 17:00:00 PST
01/14/2022 17:00:00 PST | 01/16/2022 17:00:00 PST
01/21/2022 17:00:00 PST | 01/23/2022 17:00:00 PST
01/28/2022 17:00:00 PST | 01/30/2022 17:00:00 PST
02/04/2022 17:00:00 PST | 02/06/2022 17:00:00 PST
02/11/2022 17:00:00 PST | 02/13/2022 17:00:00 PST
02/18/2022 17:00:00 PST | 02/20/2022 17:00:00 PST
02/25/2022 17:00:00 PST | 02/27/2022 17:00:00 PST
03/04/2022 17:00:00 PST | 03/06/2022 17:00:00 PST
03/11/2022 17:00:00 PST | 03/13/2022 17:00:00 PDT
03/18/2022 17:00:00 PDT | 03/20/2022 17:00:00 PDT
03/25/2022 17:00:00 PDT | 03/27/2022 17:00:00 PDT
...
--timestamptz type.
SELECT
(day + interval '17:30') AS start,
(day + interval '17:30' + interval '2 days') AS
END
FROM
generate_series(date '2022-10-01', date '2022-12-31', interval '1' day) _ (day)
WHERE
EXTRACT(ISODOW FROM day) = 5;
--timestamp type.
SELECT
(day + interval '17:30')::timestamp AS start,
(day + interval '17:30' + interval '2 days')::timestamp AS
END
FROM
generate_series(date '2022-10-01', date '2022-12-31', interval '1' day) _ (day)
WHERE
EXTRACT(ISODOW FROM day) = 5;
I do checked the calendar, it works.
I am trying to figure out the minimum time elapsed between two columns, grouped by values in a third column
ID
Start Time
End Time
1
2021-08-22 00:00:00
2021-08-24 00:00:00
1
2021-08-21 00:00:00
2021-08-24 00:00:00
2
2021-08-22 00:00:00
2021-08-24 00:00:00
2
2021-08-21 00:00:00
2021-08-24 00:00:00
3
2021-08-22 00:00:00
2021-08-24 00:00:00
3
2021-08-21 00:00:00
2021-08-24 00:00:00
From this table, I would like to get the results:
ID
Elapsed Time
1
48 hours
2
48 hours
3
48 hours
Currently I have this SQL function
SELECT ID, datediff(hour, Start Time, End Time) as diff
FROM t
WHERE
MIN(diff)
GROUP BY ID
Jacob, this should give you the results you are looking for:
SELECT
ID,
MIN(DATEDIFF (HOUR, StartTime, EndTime)) AS diff
FROM
t
GROUP BY
ID;
I have the time and the values in the data base. I need to calculate for a given month the average during each hour i.e.
YYYY-mm-dd (the day can be omitted)
2021-01-01 00:00:00 value=avg(values from 00:00:00 until 00:59:59 for every day of this month at this hour interval)
2021-01-01 01:00:00 value=avg(values from 01:00:00 until 01:59:59 idem as above)
...
2021-01-01 23:00:00 value=avg(values from 23:00:00 until 23:59:59)
2021-02-01 00:00:00 value=avg(values from 00:00:00 until 00:59:59)
2021-02-01 01:00:00 value=avg(values from 01:00:00 until 01:59:59)
...
2021-02-01 23:00:00 value=avg(values from 23:00:00 until 23:59:59)
...
You can use date_trunc('hour', datestamp) in a GROUP BY statement, something like this.
SELECT DATE_TRUNC('hour', datestamp) hour_beginning, AVG(value) average_value
FROM mytable
WHERE datestamp >= '2021-01-01'
AND datestamp < '2021-02-01'
GROUP BY DATE_TRUNC('hour', datestamp)
ORDER BY DATE_TRUNC('hour', datestamp)
To generalize, in place of DATE_TRUNC you can use any injective function.
You could use
to_char(datestamp, 'YYYY-MM-01 HH24:00:00')
to get one result row per hour for every month in your date range.
SELECT to_char(datestamp, 'YYYY-MM-01 HH24:00:00') hour,
AVG(value) average_value
FROM mytable
GROUP BY to_char(datestamp, 'YYYY-MM-01 HH24:00:00')
ORDER BY to_char(datestamp, 'YYYY-MM-01 HH24:00:00')
Based on this answer by Burak Arslan
SELECT date_trunc('month', txn_date) AS txn_month, sum(amount) as monthly_sum
FROM yourtable
GROUP BY txn_month
Is there a way to get months that have no results to show in the query?
So let's say I have :
id transDate Product Qty
1234 04/12/2019 ABCD 2
1245 04/05/2019 ABCD 1
1231 02/07/2019 ABCD 6
I also need to the the third Month returns with a 0 value
MonthYear totalQty
02/2019 6
03/2019 0
04/2019 3
Thanks,
---- UPDATE ---
Here is the final query that that gets last 24 months from the current date. with year and month ready for any charts.
Thanks to #a_horse_with_no_name
SELECT
--ONLY USE THE NEXT LINE IF YOU NEED TO HAVE THE ID IN YOUR RESULT
CASE WHEN t."ItemId" IS NULL THEN 10607 ELSE t."ItemId" END AS "ItemId",
TO_CHAR(y."transactionDate", 'yyyy-mm-dd') AS txn_month,
TO_CHAR(y."transactionDate", 'yyyy') AS "Year",
TO_CHAR(y."transactionDate", 'Mon') AS "Month",
-coalesce(SUM(t."transactionQty"),0) AS "TotalSold"
FROM generate_series(
TO_CHAR(CURRENT_DATE - INTERVAL '24 month', 'yyyy-mm-01')::date ,
TO_CHAR(CURRENT_DATE, 'yyyy-mm-01')::date,
INTERVAL '1 month') as y("transactionDate")
LEFT JOIN "ItemTransactions" AS t
ON date_trunc('month', t."transactionDate") = y."transactionDate"
AND t."ItemTransactionTypeId" = 1
AND t."ItemId" = 10607
GROUP BY txn_month, "Year", "Month", t."ItemId"
ORDER BY txn_month ASC;
EXEMPLE OUTPUT
ItemId txn_month Year Month TotalSold
10607 2018-03-01 2018 Mar 2
10607 2018-04-01 2018 Apr 0
10607 2018-05-01 2018 May 8
10607 2018-06-01 2018 Jun 12
10607 2018-07-01 2018 Jul 6
10607 2018-08-01 2018 Aug 4
10607 2018-09-01 2018 Sep 6
10607 2018-10-01 2018 Oct 8
10607 2018-11-01 2018 Nov 4
10607 2018-12-01 2018 Dec 0
10607 2019-01-01 2019 Jan 2
10607 2019-02-01 2019 Feb 3
10607 2019-03-01 2019 Mar 4
10607 2019-04-01 2019 Apr 1
10607 2019-05-01 2019 May 4
10607 2019-06-01 2019 Jun 3
10607 2019-07-01 2019 Jul 5
10607 2019-08-01 2019 Aug 6
10607 2019-09-01 2019 Sep 6
10607 2019-10-01 2019 Oct 6
10607 2019-11-01 2019 Nov 3
10607 2019-12-01 2019 Dec 0
10607 2020-01-01 2020 Jan 4
10607 2020-02-01 2020 Feb 2
10607 2020-03-01 2020 Mar 0
Left join to a list of months:
SELECT t.txn_month,
coalesce(sum(yt.amount),0) as monthly_sum
FROM generate_series(date '2019-02-01', date '2019-04-01', interval '1 month') as t(txn_month)
left join yourtable yt on date_trunc('month', yt.transdate) = t.txn_month
GROUP BY t.txn_month
Online example
In your actual query you need to move the conditions from the WHERE clause to the JOIN condition. Putting them into the WHERE clause turns the outer join back into an inner join:
SELECT t."ItemId",
y."transactionDate" AS txn_month,
-coalesce(SUM(t."transactionQty"),0) AS "TotalSold"
FROM generate_series(date '2018-01-01', date '2020-04-01', INTERVAL '1 month') as y("transactionDate")
LEFT JOIN "ItemTransactions" AS t
ON date_trunc('month', t."transactionDate") = y."transactionDate"
AND t."ItemTransactionTypeId" = 1
AND t."ItemId" = 10606
-- this WHERE clause isn't really needed because of the date values provided to generate_series()
WHERE AND y."transactionDate" >= NOW() - INTERVAL '2 year'
GROUP BY txn_month, t."ItemId"
ORDER BY txn_month DESC;
I'm trying to set the minutes and seconds to 00 in a date, I use formatdatetime(date, 'Y-MM-dd HH:00:00') and it works fine with normal dates, but trying with formatdatetime('2017-01-01 12:27:27', 'Y-MM-dd HH:00:00') the result is 2016-01-01 12:00:00 instead of 2017-01-01 12:00:00.
Why?
Here is some sample code:
CREATE TABLE test_table (
timestamp DATETIME(3) NOT NULL
);
INSERT INTO test_table
VALUES
('2017-01-01 12:27:27'),
('2017-01-02 12:27:27'),
('2017-01-03 12:27:27');
SELECT FORMATDATETIME(timestamp, 'Y-MM-dd HH:00:00')
FROM test_table;
Result is:
FORMATDATETIME(TIMESTAMP, 'Y-MM-dd HH:00:00')
2016-01-01 12:00:00
2017-01-02 12:00:00
2017-01-03 12:00:00
Why this behaviour?
I tried with H2 1.4.192/JDK 1.7.0_80 and H2 1.4.195/JRE 1.8.0_74.
Output from SELECT timestamp FROM test_table is normal:
TIMESTAMP ▼
2017-01-01 12:27:27.0
2017-01-02 12:27:27.0
2017-01-03 12:27:27.0
EDIT:
I've found the problem, it seems to be the locale:
Output from SELECT FORMATDATETIME(timestamp, 'Y-MM-dd HH:00:00 z', 'it', 'GMT') FROM test_table:
FORMATDATETIME(TIMESTAMP, 'Y-MM-dd HH:00:00 z', 'it')
2016-01-01 12:00:00 CET
2017-01-02 12:00:00 CET
2017-01-03 12:00:00 CET
Output from SELECT FORMATDATETIME(timestamp, 'Y-MM-dd HH:00:00 z', 'en', 'GMT') FROM test_table:
FORMATDATETIME(TIMESTAMP, 'Y-MM-dd HH:00:00 z', 'en')
2017-01-01 12:00:00 CET
2017-01-02 12:00:00 CET
2017-01-03 12:00:00 CET
With it locale it subtract one year at 01/01/2017, while with en locale it doesn't. Can someone explain me why this occurs? Shouldn't locale change only data rapresentation?
Ok issue solved, it was my misunderstanding on date formatting:
H2 uses, as suggested by hendrik in his comment, Java SimpleDateFormat to format dates. Following SimpleDateFormat formats, 'Y' (uppercase) stands for week year (the year to whom the week belongs).
Now 01/01/2017 was a Sunday. In the Italian locale (used on my machine), Sunday is considered the last day of the week (so the week belongs to the past year, 2016), while in the international standard (and in the en locale too), Sunday is the first day of week, then belonging to 2017.