How to fill in missing months? - sqlite

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;

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 Unpivot this table?

Base table
I have been trying out unpivot but to no avail. My desired output should be:
Desired output
First, you need to unpivot your intial data in order to get rows per month and per another new column containing values for both columns A and B.
Then, you can easily pivot the resulting data to get the desired output format.
WITH Your_test_cases (MONTH, a, b) AS (
SELECT
to_char(add_months(TRUNC(SYSDATE, 'YEAR'), LEVEL - 1), 'FMMONTH', 'nls_date_language=ENGLISH') MONTH
, LEVEL a
, LEVEL b
FROM dual
CONNECT BY LEVEL <= 12
)
SELECT *
FROM Your_test_cases
UNPIVOT (
VAL_A_AND_B_IN_ONE_COL FOR COL IN (
A AS 'A'
, B AS 'B'
)
)
PIVOT (
MIN(VAL_A_AND_B_IN_ONE_COL) FOR MONTH IN (
'JANUARY' as JANUARY,
'FEBRUARY' as FEBRUARY,
'MARCH' as MARCH,
'APRIL' as APRIL,
'MAY' as MAY,
'JUNE' as JUNE,
'JULY' as JULY,
'AUGUST' as AUGUST,
'SEPTEMBER' as SEPTEMBER,
'OCTOBER' as OCTOBER,
'NOVEMBER' as NOVEMBER,
'DECEMBER' as DECEMBER
)
)
ORDER BY 1
;
demo

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

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;

SQL Group Distinct Count

I have the following table
User ID Start Date End Date
-------------------------------------
John Doe Mar 11 2011 May 28 2011
Robret S Mar 21 2011 Jun 29 2011
Tina T Feb 01 2011 August 20 2011
I want to show how many people I have available for the past 6 months, even if the month has no people. How can this be possible. I know I have to do grouping and use distinct count.
Expected Output:
February = 1 Resource
March = 3 Resources
April = 3 Resources
May = 3 Resources
June = 2 Resources
July = 1 Resource
August = 1 Resource
With Calendar As
(
Select Cast('20110501' As DateTime) As [Date]
Union All
Select DateAdd(m,-1,[Date])
From Calendar
Where [Date] > DateAdd(m,-5,'20110501')
)
Select DateName(m, C.Date) + ' ' + Cast(Year(C.Date) As char(4))
, Case Count(*)
When 1 Then Cast(Count(*) As varchar(10)) + ' Resource'
Else Cast(Count(*) As varchar(10)) + ' Resources'
End
From Calendar As C
Left Join MyTable As T
On C.Date Between T.StartDate And T.EndDate
Group By C.Date
Results:
December 2010 | 1 Resource
January 2011 | 1 Resource
February 2011 | 1 Resource
March 2011 | 1 Resource
April 2011 | 3 Resources
May 2011 | 3 Resources
You will need the existing data records for the last 6 months, so that you can merge the two sets of data. You can generate the last 6 months in a CTE and do a left join with your data. That will allow you to show the last 6 months even if you have no data.
I don't think you can do what you want using a "simple" select statement (even using GROUPing etc.) The following is off the top of my head, so you'll have to experiment with it a little, and preferably read Joe Celko's excellent SQL for Smarties book.
You need to create a second table that contains all of your months (start/end dates). You only need one table for all types of similar queries, and it must contain all the months in the date ranges your interested in querying:
CREATE TABLE months (id, start DATE, end DATE);
INSERT INTO months (id, start, end)
values ( (1, 2011-01-01, 2011-01-31),
(2, 2011-02-01, 2011-02-28), ...);
You then LEFT OUTER JOIN from your user table to this month table. That will give you a row for each user for each month they were available, which you can GROUP as required:
SELECT months.id, COUNT(user.id)
FROM months LEFT OUTER JOIN users
ON user.start_date < months.end
AND user.end_date > months.start
GROUP BY months.id;
Hope that helps.
WITH resources AS (
SELECT
Date = DATEADD(month, v.number, [Start Date])
FROM atable t
INNER JOIN master.dbo.spt_values v ON v.type = 'P'
AND v.number BETWEEN 0 AND DATEDIFF(month, t.[Start Date], t.[End Date])
)
SELECT
Month = DATENAME(month, Date),
ResourceCount = CAST(COUNT(*) AS varchar(30)) +
CASE COUNT(*) WHEN 1 THEN ' Resource' ELSE ' Resources' END
FROM resources
WHERE Date > DATEADD(month, -6, DATEADD(day, -DAY(GETDATE()), GETDATE()))
GROUP BY YEAR(Date), MONTH(Date), DATENAME(month, Date)
ORDER BY YEAR(Date), MONTH(Date)

Resources