Calculate first day and last day of week of current month - sqlite

I want to calculate first date and last date of a week for the current month given current datetime.
For example, 01 December 2017 is first day and 02 December 2017 is last day of week for that week for the month of December. In the same week of year, 26 November 2017 is first day of week and
30 November 2017 is last day of week for previous month.
So if today is 01 December 2017, I should get 01-02 from current datetime , instead of 26-02 range.
PS: I am trying to do this thing in a query of Sqlite. I want to group some values on a weekly basis. The days of the week should belong to current month. Hence this requirement. Till now this is my query.
SELECT SUM(amount) as Y,
strftime('%d', datetime(createdOn/1000, 'unixepoch'), '-'||strftime('%w', datetime(createdOn/1000, 'unixepoch'))||' day' )
||'-'||
strftime('%d', datetime(createdOn/1000, 'unixepoch'), '+'||(6-strftime('%w', datetime(createdOn/1000, 'unixepoch')))||' day' ) AS X
FROM my_table
GROUP BY strftime('%d', datetime(createdOn/1000, 'unixepoch'), '-'||strftime('%w', datetime(createdOn/1000, 'unixepoch'))||' day' )
||'-'||
strftime('%d', datetime(createdOn/1000, 'unixepoch'), '+'||(6-strftime('%w', datetime(createdOn/1000, 'unixepoch')))||' day' )
How to achieve that; given current datetime.

Common table expressions are useful to hold intermediate results:
WITH t1 AS (
SELECT amount, date(createdOn / 1000, 'unixepoch', 'localtime') AS date
FROM my_table
),
t2 AS (
SELECT *,
date(date, 'weekday 6') AS end_of_week,
date(date, 'weekday 6', '-6 days') AS start_of_week
FROM t1
),
t3 AS (
SELECT *,
strftime('%m', date ) AS month,
strftime('%m', start_of_week) AS sowk_month,
strftime('%m', end_of_week ) AS eowk_month,
date(date, 'start of month') AS start_of_month,
date(date, '+1 month', 'start of month', '-1 day') AS end_of_month
FROM t2
),
t4 AS (
SELECT *,
CASE WHEN sowk_month = month THEN start_of_week
ELSE start_of_month
END AS week_in_month_start,
CASE WHEN eowk_month = month THEN end_of_week
ELSE end_of_month
END AS week_in_month_end
FROM t3
),
t5 AS (
SELECT amount,
week_in_month_start || '-' || week_in_month_end AS week_in_month
FROM t4
)
SELECT SUM(amount) AS Y,
week_in_month
FROM t5
GROUP BY week_in_month;

Related

Get the MAX(value) using fiscal type years ie; 2016/2017, etc

My calendar year runs from 07-01-(of one year) to 06-30-(of the next year).
My SQLITE DB has a Timestamp column and it's data type is datetime and stores the timestamp as 2023-09-01 00:00:00.
What I'm trying to do is get the MAX date of the latest snowfall. For example, with my seasonal years beginning July-01 (earliest) and ending June 30 (latest), I want to find only the latest (MAX) date snowfall was recorded, regardless of the year, based on the month.
Say if out of five years (2017 to 2022) worth of data in the database and it snowed Mar 15, 2020. And there was no date greater than than this one in any year, then this would be the latest date regardless which year it fell.
I've been trying many variations of the below query. This query says it runs with no mistakes and returns "null" values. I'm using SQLITE DB Browser to write and test the query.
SELECT Timestamp, MAX(strftime('%m-%d-%Y', Timestamp)) AS lastDate,
snowDepth AS lastDepth FROM DiaryData
WHERE lastDepth <> 0 BETWEEN strftime('%Y-%m-%d', Timestamp,'start of year', '+7 months')
AND strftime('%Y-%m-%d', Timestamp, 'start of year', '+1 year', '+7 months', '- 1 day')
ORDER BY lastDate LIMIT 1
and this is what's in my test database:
Timestamp snowFalling snowLaying snowDepth
2021-11-10 00:00:00 0 0 7.2
2022-09-15 00:00:00 0 0 9.5
2022-12-01 00:00:00 1 0 2.15
2022-10-13 00:00:00 1 0 0.0
2022-05-19 00:00:00 0 0 8.82
2023-01-11 00:00:00 0 0 3.77
If it's running properly I should expect:
Timestamp
lastDate
lastDepth
2022-05-19 00:00:00
05-19-2022
8.82
What am I missing or is this not possible in SQLITE? Any help would be appreciative.
Use aggregation by fiscal year utilizing SQLite's feature of bare columns:
SELECT Timestamp,
strftime('%m-%d-%Y', MAX(Timestamp)) AS lastDate,
snowDepth AS lastDepth
FROM DiaryData
WHERE snowDepth <> 0
GROUP BY strftime('%Y', Timestamp, '+6 months');
See the demo.
I'd get season for each record first, snowfall date relative to record's season start date after this, and largest snowfall date relative to record's season start date finally:
with
data as (
select
*
, case
when cast(strftime('%m', "Timestamp") as int) <= 7
then strftime('%Y-%m-%d', "Timestamp", 'start of year', '-1 year', '+6 months')
else strftime('%Y-%m-%d', "Timestamp", 'start of year', '+6 months')
end as "Season start date"
from DiaryData
where 1==1
and "snowDepth" <> 0.0
)
, data2 as (
select
*
, julianday("Timestamp") - julianday("Season start date")
as "Showfall date relative to season start date"
from data
)
, data3 as (
select
"Timestamp"
, "snowFalling"
, "snowLaying"
, "snowDepth"
from data2
group by null
having max("Showfall date relative to season start date")
)
select
*
from data3
demo
You can use the ROW_NUMBER window function to address this problem, yet need to apply a subtle tweak. In order to account for fiscal years, you can partition on the year for timestamps slided 6 months further. In this way, ranges like [2021-01-01, 2021-12-31] will instead be slided to [2021-06-01, 2022-05-31].
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(
PARTITION BY STRFTIME('%Y', DATE(Timestamp_, '+6 months'))
ORDER BY Timestamp_ DESC ) AS rn
FROM tab
)
SELECT Timestamp_,
STRFTIME('%d-%m-%Y', Timestamp_) AS lastDate,
snowDepth AS lastDepth
FROM cte
WHERE rn = 1
Check the demo here.

how to get the last weekday of the month in sqllite

given a date in sqllite I would like to find the last weekday of the month.
for example, if given 11/17/2021 then the last weekday of the month is 11/30. however, if given 4/30/2022 which falls on a saturday then the last weekday of the month is 4/29/2022.
i try the following but this only gives me the last day of the month which can be either a weekend of weeday.
SELECT date('now','start of month','+1 month','-1 day');
i am looking for the last weekday of the month given a specific date in sql lite.
can someone help me figure this out? thanks in advance
One solution is to calculate the day of the week for the end of the month, and adjust the result based off that value:
SELECT
CASE strftime('%w', date('now', 'start of month','+1 month','-1 day'))
WHEN '0' THEN date('now', 'start of month','+1 month','-3 day')
WHEN '6' THEN date('now', 'start of month','+1 month','-2 day')
ELSE date('now', 'start of month','+1 month','-1 day')
END;
The following will take a US formatted date (m/d/y) and output the last weekday in the month in the same US format:-
WITH
cte_inputdate AS (SELECT '12/19/2022' /*<<<<<<<<<<< change as required */ AS inputdate),
cte_convertdate AS
(SELECT
CASE
/* m/d/yyyy */
WHEN substr(inputdate,2,1) = '/'
AND substr(inputdate,4,1) = '/'
THEN substr(inputdate,5,4)||'-0'||substr(inputdate,1,1)||'-0'||substr(inputdate,3,1)
/* m/dd/yyyy */
WHEN substr(inputdate,2,1) = '/'
AND substr(inputdate,5,1) = '/'
THEN substr(inputdate,6,4)||'-0'||substr(inputdate,1,1)||'-'||substr(inputdate,3,2)
/* mm/d/yyyy */
WHEN substr(inputdate,3,1) = '/'
AND substr(inputdate,5,1) = '/'
THEN substr(inputdate,6,4)||'-'||substr(inputdate,1,2)||'-0'||substr(inputdate,4,1)
/* mm/dd/yyyy */
ELSE substr(inputdate,7,4)||'-'||substr(inputdate,1,2)||'-'||substr(inputdate,4,2)
END AS inputdate
FROM cte_inputdate
),
cte_lastweekdayofmonth AS
(SELECT *,
CASE CAST(strftime('%w',inputdate,'start of month','+1 month','-1 day') AS INTEGER)
WHEN 0 THEN date(inputdate,'start of month','+1 month','-3 day')
WHEN 6 THEN date(inputdate,'start of month','+1 month','-2 day')
ELSE date(inputdate,'start of month','+1 month','-1 day')
END AS lastweekdayofmonth
FROM cte_convertdate
)
/* Extract lastweekdayofthemonth converting it to m/d/yyyy format */
SELECT
CASE WHEN substr(lastweekdayofmonth,6,1) = '0' THEN substr(lastweekdayofmonth,7,1) ELSE substr(lastweekdayofmonth,6,2) END||'/'||
CASE WHEN substr(lastweekdayofmonth,9,1) = '0' THEN substr(lastweekdayofmonth,10,1) ELSE substr(lastweekdayofmonth,9,2) END||'/'||
substr(lastweekdayofmonth,1,4) AS lastweekdayofmonth
FROM cte_lastweekdayofmonth
;
e.g
for 11/17/2021 then :-
for 4/30/2022 then :-

Day and night averages SQlite

I have a db with many years of ambient temperature, I need to make the average temperature from every month from 06:00 to 20:00 (day) and night. What I already made is
SELECT
STRFTIME('%Y', fecha) AS year,
STRFTIME('%m', fecha) AS month,
AVG(T10_AVG) AS Temp_prom,
FROM GER
GROUP BY STRFTIME('%Y', fecha), STRFTIME('%m', fecha)
But I have no idea how to include the day and night averages.
If you want to get those averages in the same monthly output record, you cannot add another GROUP BY column.
You have to compute these values with separate subqueries:
SELECT strftime('%Y', fecha) AS year,
strftime('%m', fecha) AS month,
(SELECT AVG(T10_AVG)
FROM Ger AS g2
WHERE g2.fecha GLOB strftime('%Y-%m*', Ger.fecha)
AND (g2.hour >= '06' AND g2.hour < '20')
) AS avg_day,
(SELECT AVG(T10_AVG)
FROM Ger AS g2
WHERE g2.fecha GLOB strftime('%Y-%m*', Ger.fecha)
AND (g2.hour < '06' OR g2.hour >= '20')
) AS avg_night,
AVG(T10_AVG) AS Temp_prom
FROM Ger
GROUP BY year, month

Calculate Average for the month in SQlite

I have an Sqlite table as below called balance history:
Table Balance History
Date Amount
2013-11-01 16:26:52 1000
2013-11-15 13:20:52 2000
2013-11-27 12:26:55 3000
I would like to calculate the average for the month.
**
The Expected OutPut will be 1666.67
**
Which will be (1000 * 14 days + 2000 * 12 days + 3000 * 4 days)/30 days
= (14000 + 24000 + 12000)/30 = 1666.67
How can I achieve this in SQlite? any help will be appreciated.
thanks
First, we have to compute the start and the end of each interval.
The simple query (SELECT Date AS "From", Amount FROM BalanceHistory WHERE Date GLOB '2013-11*') gets the start for each interval in the month.
(GLOB is case sensitive and thus allows to use a normal index; LIKE would require a special case-insensitive index.)
If there is no record for the first day of the month, the part after the UNION ALL adds the last record of the previous month and changes the day to the 1st.
The COALESCE computes the end of the interval.
The subquery gets the next date from the table, if there is one in the current month.
If there is no such record, it takes the first day of the next month:
SELECT date("From") AS "From",
COALESCE((SELECT date(MIN(Date))
FROM BalanceHistory
WHERE Date > "From"
AND Date GLOB '2013-11*'),
date('2013-11-01', '+1 month')
) AS "To",
"Amount"
FROM (SELECT Date AS "From",
Amount
FROM BalanceHistory
WHERE Date GLOB '2013-11*'
UNION ALL
SELECT *
FROM (SELECT date(Date, '+1 month', 'start of month'),
Amount
FROM BalanceHistory
WHERE Date < '2013-11'
AND NOT EXISTS (SELECT 1
FROM BalanceHistory
WHERE Date GLOB '2013-11-01*')
ORDER BY Date DESC
LIMIT 1)
);
From To Amount
---------- ---------- ------
2013-11-01 2013-11-15 1000
2013-11-15 2013-11-27 2000
2013-11-27 2013-12-01 3000
We can then wrap this in another query to compute the number of days, and add them up.
The strftime calculates the last day of the month, i.e., the number of days:
SELECT SUM((julianday("To") - julianday("From")) * Amount) /
strftime('%d', '2013-11-01', '+1 month', '-1 day') AS MonthAvg
FROM (SELECT date("From") AS "From",
COALESCE((SELECT date(MIN(Date))
FROM BalanceHistory
WHERE Date > "From"
AND Date GLOB '2013-11*'),
date('2013-11-01', '+1 month')
) AS "To",
"Amount"
FROM (SELECT Date AS "From",
Amount
FROM BalanceHistory
WHERE Date GLOB '2013-11*'
UNION ALL
SELECT *
FROM (SELECT date(Date, '+1 month', 'start of month'),
Amount
FROM BalanceHistory
WHERE Date < '2013-11'
AND NOT EXISTS (SELECT 1
FROM BalanceHistory
WHERE Date GLOB '2013-11-01*')
ORDER BY Date DESC
LIMIT 1)
)
)
And while we're at it, we can wrap this in yet another query to replace all those 2013-11 strings with the month as read from the table.
This allows as to compute this for every month:
SELECT Month,
(SELECT SUM((julianday("To") - julianday("From")) * Amount) /
strftime('%d', Month || '-01', '+1 month', '-1 day')
FROM (SELECT date("From") AS "From",
COALESCE((SELECT date(MIN(Date))
FROM BalanceHistory
WHERE Date > "From"
AND Date GLOB Month || '*'),
date(Month || '-01', '+1 month')
) AS "To",
"Amount"
FROM (SELECT Date AS "From",
Amount
FROM BalanceHistory
WHERE Date GLOB Month || '*'
UNION ALL
SELECT *
FROM (SELECT date(Date, '+1 month', 'start of month'),
Amount
FROM BalanceHistory
WHERE Date < Month
AND NOT EXISTS (SELECT 1
FROM BalanceHistory
WHERE Date GLOB Month || '-01*')
ORDER BY Date DESC
LIMIT 1)
)
)
) AS MonthAvg
FROM (SELECT DISTINCT strftime('%Y-%m', Date) AS Month
FROM BalanceHistory)
ORDER BY 1

How to fill in missing months?

I've got the following SQL:
SELECT STRFTIME('%m', date) ord,
CASE STRFTIME('%m', date) WHEN '01' THEN 'January'
WHEN '02' THEN 'Febuary'
WHEN '03' THEN 'March'
WHEN '04' THEN 'April'
WHEN '05' THEN 'May'
WHEN '06' THEN 'June'
WHEN '07' THEN 'July'
WHEN '08' THEN 'August'
WHEN '09' THEN 'September'
WHEN '10' THEN 'October'
WHEN '11' THEN 'November'
WHEN '12' THEN 'December'
ELSE '' END AS month,
count(*) AS count
FROM events
WHERE type='Birth' AND
date <> ''
GROUP BY month,ord
ORDER BY ord
This gives me results similar to:
ord month count
01 January 1
02 Febuary 1
03 March 3
05 May 4
07 July 2
08 August 2
09 September 2
11 November 4
But as you can see it has gaps. Is there any way to fill in the missing months with a 0 count?
Study outer join.
Run the following once:
CREATE TABLE months (
mm char(2),
name char(10)
);
INSERT INTO months VALUES ('01', 'January');
INSERT INTO months VALUES ('02', 'Febuary');
INSERT INTO months VALUES ('03', 'March');
INSERT INTO months VALUES ('04', 'April');
INSERT INTO months VALUES ('05', 'May');
INSERT INTO months VALUES ('06', 'June');
INSERT INTO months VALUES ('07', 'July');
INSERT INTO months VALUES ('08', 'August');
INSERT INTO months VALUES ('09', 'September');
INSERT INTO months VALUES ('10', 'October');
INSERT INTO months VALUES ('11', 'November');
INSERT INTO months VALUES ('12', 'December');
And run this:
SELECT m.mm AS ord,
m.name AS month,
count(e.date) AS count
FROM months m LEFT OUTER JOIN events e
ON strftime('%m', e.date) = m.mm
WHERE e.type='Birth' AND
e.date <> ''
GROUP BY month,ord
ORDER BY ord;

Resources