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

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.

Related

Impala - Working hours between two dates in impala

I have two time stamps #starttimestamp and #endtimestamp. How to calculate number of working hours between these two
Working hours is defined below:
Mon- Thursday (9:00-17:00)
Friday (9:00-13:00)
Have to work in impala
think i found a better solution.
we will create a series of numbers using a large table. You can get a time dimension type table too. Make it doenst get truncated. I am using a large table from my db.
Use this series to generate a date range between start and end date.
date_add (t.start_date,rs.uniqueid) -- create range of dates
join (select row_number() over ( order by mycol) as uniqueid -- create range of unique ids
from largetab) rs
where end_date >=date_add (t.start_date,rs.uniqueid)
Then we will calculate total hour difference between the timestamp using unix timestamp considering date and time.
unix_timestamp(endtimestamp - starttimestamp )
Exclude non working hours like 16hours on M-T, 20hours on F, 24hours on S-S.
case when dayofweek ( dday) in (1,7) then 24
when dayofweek ( dday) =5 then 20
else 16 end as non work hours
Here is complete SQL.
select
end_date, start_date,
diff_in_hr - sum(case when dayofweek ( dday) in (1,7) then 24
when dayofweek ( dday) =5 then 20
else 16 end ) total_workhrs
from (
select (unix_timestamp(end_date)- unix_timestamp(start_date))/3600 as diff_in_hr , end_date, start_date,date_add (t.start_date,rs.uniqueid) as dDay
from tdate t
join (select row_number() over ( order by mycol) as uniqueid from largetab) rs
where end_date >=date_add (t.start_date,rs.uniqueid)
)rs2
group by 1,2,diff_in_hr

SQLite How can i create in sqlite a Table with 365 Rows and how can i insert the dates?

"How can i create in sqlite a Table with 365 Rows and how can i insert the dates?"
For example:
Table MyTable
id month date
1 jan 2021-01-01
2 jan 2021-01-02
3 jan 2021-01-03
.
.
.
365 dec 2021-12-31
How can i create this automatically ?
Thanks
You may reach this using recursive query
create table days as
with recursive qq as (
select 1 id, 'jan' month, '2021-01-01' date_col union all
select id + 1, substr('janfebmaraprmayjunjulaugsepoctnovdec', 1 + 3 *
strftime('%m', date(date_col, '+1 day')), -3), date(date_col, '+1 day')
from qq
where id <= 364
)
The monstrous line
substr('janfebmaraprmayjunjulaugsepoctnovdec', 1 + 3 * strftime('%m', date(date_col, '+1 day')), -3)
will cut the name of the month you need from the line based on the month number of the date. Ths one is needed because SQLIte seems to be not able to get month name from date.
There were suggestions of how one can get month names from date but it did not worked for me. Try those if you want
Here's an example on dbfiddle

SQLite Get Weekday Date from Field

I am trying to get the equivalent of this query in SQL in SQLite
SELECT DATE_ADD(now(),INTERVAL (DAYOFWEEK(S.start_date)+1) DAY) AS pymtdate FROM subscriptions
The purpose of this is that I have a start date and a weekly payment. I want to calculate the weekly payments for the next N days (in the example above this is for one week) for a number of start dates. In SQLite the nearest I can get to is
SELECT date('now', 'weekday stftime('%w', S.start_date)') AS pymtdate FROM subscriptions
but this isn't working as it blows out when I try to combine the weekday with a calculated value.
Any suggestions?
I did find the answer using this post: sqlite return as day of week
SELECT S.subscription_id AS subid,
case cast (strftime('%w',S.start_date) as integer)
when 0 then date('now' , 'weekday 0')
when 1 then date('now' , 'weekday 1')
when 2 then date('now' , 'weekday 2')
when 3 then date('now' , 'weekday 3')
when 4 then date('now' , 'weekday 4')
when 5 then date('now' , 'weekday 5')
else date('now' , 'weekday 6') end as pymtdate
FROM subscriptions AS S
In SQL, anything 'between quotes' is a string.
You do not want the string strftime(...), you want to execute this function, so you must write it outside the quotes (and append its return value to the other string):
SELECT date('now', 'weekday ' || strftime('%w', S.start_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

T-SQL - Determine number of specific days between 2 dates based on sales

The Problem: Given a day of the week (1, 2, 3, 4, 5, 6, 7), a starting date and an ending date, compute the number of times the given day of the week appears between the starting and ending dates not inclusive of a date for which there were no sales.
Context:
Table "Ticket" has the following structure and sample content:
i_ticket_id c_items_total dt_create_time dt_close_time
----------------------------------------------------------------------------
1 8.50 '10/1/2012 10:23:00' '10/1/2012 11:05:05'
2 10.50 '10/1/2012 11:00:00' '10/1/2012 11:45:05'
3 8.50 '10/2/2012 08:00:00' '10/2/2012 09:25:05'
4 8.50 '10/4/2012 08:00:00' '10/4/2012 09:25:05'
5 7.50 '10/5/2012 13:22:23' '10/5/2012 14:33:27'
.
.
233 6.75 '10/31/2012 23:20:00' '10/31/2012 23:55:39'
Details
There may or may not be any tickets for one or more days during a month. (i.e. the place was closed that/those day/s)
Days in which the business is closed are not regular. There is no predictable pattern.
Based on Get number of weekdays (Sundays, Mondays, Tuesdays) between two dates SQL,
I have derived a query which returns the number of times a given day of the week occurs between the start date and the end date:
DECLARE #dtStart DATETIME = '10/1/2013 04:00:00'
DECLARE #dtEnd DATETIME = '11/1/2013 03:59:00'
DECLARE #day_number INTEGER = 1
DECLARE #numdays INTEGER
SET #numdays = (SELECT 1 + DATEDIFF(wk, #dtStart, #dtEnd)-
CASE WHEN DATEPART(weekday, #dtStart) #day_number THEN 1 ELSE 0 END -
CASE WHEN DATEPART(weekday, #dtEnd) <= #day_number THEN 1 ELSE 0 END)
Now I just need to filter this so that any zero-dollar days are not included in the count. Any help you can provide to add this filter based on the contents of the tickets table is greatly appreciated!
If I understand correctly, you can use a calendar table to count the number of days where the day of week is n and between the start and end and is a date that has ticket sales, which I guess is when the date exists in tickets and has the sum(c_items_total) > 0
WITH cal AS
(
SELECT cast('2012-01-01' AS DATE) dt, datepart(weekday, '2012-01-01') dow
UNION ALL
SELECT dateadd(day, 1, dt), datepart(weekday, dateadd(day, 1, dt))
FROM cal
WHERE dt < getdate()
)
SELECT COUNT(1)
FROM cal
WHERE dow = 5
AND dt BETWEEN '2012-04-01' AND '2012-12-31'
AND EXISTS (
SELECT 1
FROM tickets
WHERE cast(dt_create_time AS DATE) = dt
GROUP BY cast(dt_create_time AS DATE)
HAVING sum(c_items_total) > 0
)
OPTION (MAXRECURSION 0)
SQLFiddle

Resources