how to get the last weekday of the month in sqllite - sqlite

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 :-

Related

How convert a string to date in SQLite query? (WatermelonDB)

I'm using watermelonDB in my application, however watermelon save Date as number in Database.
I intend get all items on month or year, howerver I can't compare filter's date with the date of database.
`SELECT * FROM invoices WHERE
${byYear
? `date(${dateString}, 'start of year') = date(created_at, 'start of year')`
: `date(${dateString}, 'start of month') = date(created_at, 'start of month') AND
date(${dateString}, 'start of year') = datetime(created_at, 'start of year')`
}`
Is there a option to transform created_at in "YYYY-MM-DD" with SQLite methods?
It seems that the timestamps that are stored in the table are integer numbers that represent milliseconds since 1970-01-01 00:00:00.
You can convert them to readable text dates in the format YYYY-MM-DD with:
date(created_at / 1000, 'unixepoch')
or
date(created_at / 1000, 'unixepoch', 'localtime')
The division by 1000 strips off the milliseconds.
You can apply other modifiers too, like 'start of year' or 'start of month':
date(created_at / 1000, 'unixepoch', 'localtime', 'start of year')

Calculate first day and last day of week of current month

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;

TERADATA case when statement in WHERE clause

I need to write a case statement in the WHERE clause, which is -
when current_date is 1st of Month then select data BETWEEN 1st day of prev month AND last day prev month
ELSE FROM 1st of Curr month till date. I have written this so far but it is not working. '05/01/2017' will be input date.
SELECT *
FROM MyTable
WHERE calendar_date
BETWEEN
CASE WHEN extract (day from CAST( '05/01/2017' AS DATE FORMAT 'MM/DD/YYYY')) =1 --check to see date is 1st of month
THEN ADD_MONTHS((CAST( '05/01/2017' AS DATE FORMAT 'MM/DD/YYYY') - EXTRACT(DAY FROM CAST( '05/01/2017' AS DATE FORMAT 'MM/DD/YYYY'))+1), -1) --1st of prev month
AND ADD_MONTHS(CURRENT_DATE - EXTRACT(DAY FROM CURRENT_date), 0 ) --last day prev month
ELSE CAST( '05/01/2017' AS DATE FORMAT 'MM/DD/YYYY') - EXTRACT(DAY FROM CAST( '05/01/2017' AS DATE FORMAT 'MM/DD/YYYY'))+1, 0) --else 1st of Curr mont
AND CURRENT_DATE
end
order by calendar_date
select *
from mytable
where calendar_date between case
when td_day_of_month (current_date) = 1
then current_date - interval '1' month
else td_month_begin (current_date)
end
and case
when td_day_of_month (current_date) = 1
then current_date - 1
else current_date
end
order by calendar_date
#Dudu, based on your suggestion, I was able to solve the sql. here it is:
SELECT * FROM MYTABLE
WHERE CALENDAR_DATE
BETWEEN
CASE WHEN extract (day from CAST( '05/15/2017' AS DATE FORMAT 'MM/DD/YYYY')) = 1
THEN (ADD_MONTHS((CAST( '05/15/2017' AS DATE FORMAT 'MM/DD/YYYY') - EXTRACT(DAY FROM CAST( '05/15/2017' AS DATE FORMAT 'MM/DD/YYYY'))+1), -1) )
ELSE ADD_MONTHS(CURRENT_DATE - EXTRACT(DAY FROM CURRENT_DATE)+1, 0)
END
AND
CASE WHEN extract (day from CAST( '05/15/2017' AS DATE FORMAT 'MM/DD/YYYY')) > 1
THEN CURRENT_DATE
ELSE ADD_MONTHS(CURRENT_DATE - EXTRACT(DAY FROM CURRENT_date), 0 )
END
AND REPORTNAME_MASTER_ID IN (2565,5216,5364)
order by CALENDAR_DATE

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 create report with tickets closed at certain date in Trac

I wish to create a report that would list all the tickets that were closed in a certain period of time.
The pseudo-code would be like
SELECT * FROM tickets
WHERE closed AND date_closed = 'january 2009'
The part I am unable to resolve is date_closed = 'january 2009'.
Is there a way to do it in Trac?
I am not interested in particular SQL syntax, I can write the time constrictions myself. What I am not certain about is Trac's db structure.
SELECT * FROM ticket
WHERE status='closed'
AND date(changetime,'unixepoch')
BETWEEN date('YYYY-MM-DD') /* <- here goes your start date */
AND date('YYYY-MM-DD') /* <- here goes your end date */
If you want a specific month:
SELECT * FROM ticket
WHERE status='closed'
AND date(changetime,'unixepoch')
BETWEEN date('2009-01-01','start of month')
AND date('2009-01-01','start of month','+1 month','-1 day')
Where date('2009-01-01','start of month') is the first day of the month given by date, and date('2009-01-01','start of month','+1 month','-1 day') is the last day of the month.
SELECT DISTINCT ticket.* FROM ticket, ticket_change
WHERE ticket.id = ticket_change.ticket
AND ticket_change.field = 'status'
AND ticket_change.newvalue = 'closed'
AND strftime('%m', ticket_change.time, 'unixepoch') = '01';
If you also know the year, instead of strftime you’d better use an expression like vartec’s suggested:
SELECT DISTINCT ticket.* FROM ticket, ticket_change
WHERE ticket.id = ticket_change.ticket
AND ticket_change.field = 'status'
AND ticket_change.newvalue = 'closed'
AND date(ticket_change.time,'unixepoch')
BETWEEN date('2009-01-01','start of month')
AND date('2009-01-01','start of month','+1 month','-1 day')
Also, regarding the table structure, here you go:
CREATE TABLE ticket_change (
ticket INTEGER,
time INTEGER,
author TEXT,
field TEXT,
oldvalue TEXT,
newvalue TEXT,
UNIQUE ( ticket, time, field )
);

Resources