How to Unpivot this table? - plsql

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

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.

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;

Fiscal Month Start Dates

I have a fiscal Month End date column. Based on this column I will have to calculate 12 month prior fiscal start date, 24 month prior fiscal start date and so on.
I cannot use add_months function here as below are the fscl month end dates. When a user chooses 01-JUL-2017, the 12 months prior date should start at 03-JUL-16.
If I use add months the date range would change to be 01-JUL-2016 to 01-JUL-2017 but I need it to be 03-JUL-2017 to 01-JUL-2017.
FISCAL MONTH END DATES
30-JAN-16
27-FEB-16
02-APR-16
30-APR-16
28-MAY-16
02-JUL-16
30-JUL-16
27-AUG-16
01-OCT-16
29-OCT-16
26-NOV-16
28-JAN-17
25-FEB-17
01-APR-17
29-APR-17
27-MAY-17
01-JUL-17
Considering that your table has all the dates in the sequence, You can use the LAG function with parameter 11 (i.e. 11th previous date in the sequence).Take the user input from the where clause as a bind Variable.
( WHERE TO_CHAR(DT,'DD-MON-YY') = :curr_fiscal_end_dt )
SELECT Prev_fisc_start_date FROM
(
SELECT DT, LAG(DT, 11 ) OVER ( ORDER BY DT ) + 1 Prev_fisc_start_dt FROM
Your_table
) WHERE TO_CHAR(DT,'DD-MON-YY') ='01-JUL-2017';

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