I have a table like this:
date
2020-03-11 10:41:26
2020-03-11 17:51:15
2020-03-12 10:45:28
2020-03-12 17:41:28
I want the result:
datech date cnt
2020年3月12日 2020-03-12 17:41:28 2
2020年3月11日 2020-03-11 17:51:15 2
I have tried
Select *, count(*) as cnt from (
Select (DATE_FORMAT((date),'%Y年%c月%d日')) as datech, date
from tablea where 1=1
order by date desc
) as t
GROUP BY datech order by date desc
but the result date is asc
datech date cnt
2020年3月12日 2020-03-12 10:45:28 2
2020年3月11日 2020-03-11 10:41:26 2
How can I desc date?
Thanks.
Try this:
Select datech, max(date) as date, count(*) as cnt from (
Select (DATE_FORMAT((date),'%Y年%c月%d日')) as datech, date
from tablea where 1=1
) as t
GROUP BY datech order by date desc;
Related
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.
I have a table with data like below:
Log Table:
User Id
Login Date
1
2022-01-03
1
2022-01-04
1
2022-01-10
1
2022-01-11
1
2022-01-12
1
2022-01-23
1
2022-01-25
1
2022-01-26
1
2022-01-27
1
2022-01-28
What I'm trying to do is to create a query that return rows of the latest logins by consecutive dates with var_date as parameter.
If var_date is 2022-01-29, then the result is:
User Id
Login Date
1
2022-01-25
1
2022-01-26
1
2022-01-27
1
2022-01-28
If var_date is 2022-01-30, then no result is returned, since 2022-01-29 is not in the table.
If var_date is 2022-01-24, then the query will return row with 2022-01-23 as login date.
How am I to do this in SQLite?
Thank you.
This question is a variant of gaps and islands, with the islands being clusters of records per user with continuous dates. Here is one approach using analytic functions:
WITH cte AS (
SELECT *, CASE WHEN julianday(LoginDate) -
julianday(LAG(LoginDate) OVER (PARTITION BY UserID
ORDER BY LoginDate))
> 1 THEN 1 ELSE 0 END AS counter
FROM yourTable
),
cte2 AS (
SELECT *, SUM(counter) OVER (PARTITION BY UserID ORDER BY LoginDate) AS grp
FROM cte
)
SELECT UserID, LoginDate
FROM cte2 t1
WHERE LoginDate < '2022-01-29' AND
grp = (SELECT t2.grp FROM cte2 t2
WHERE t2.UserID = t1.UserID AND t2.LoginDate = '2022-01-28');
Demo
The two CTEs generate a pseudo date group for each cluster per user. The final query returns all records less than the target date for which the group value is the same as the immediately preceding date. Hence, for dates having no immediate record for a given user, the query will return empty set.
Use a recursive CTE:
WITH cte(UserId, LoginDate) AS (
SELECT :var_user_id, :var_date
UNION ALL
SELECT UserId, date(c.LoginDate, '-1 day')
FROM cte c
WHERE EXISTS (SELECT 1 FROM tablename t WHERE t.UserId = c.UserId AND t.LoginDate = date(c.LoginDate, '-1 day'))
)
SELECT *
FROM cte
WHERE LoginDate < (SELECT MAX(LoginDate) FROM cte);
Change :var_user_id and :var_date to the values that you want for the user's id and the date.
See the demo.
I've got a table in an SQLite3 database containing account balances, but it currently only contains balances for a few specific dates:
Balance Date
Amount
2021-12-15
400
2021-12-18
500
2021-12-22
200
I need to fill in the gaps between these dates with the previous recorded balance, so e.g. 2021-12-16 and 2021-12-17 should have a balance of 400 and 2021-12-19, 2021-12-20 and 2021-12-21 should have a balance of 500.
Is there a way to fill these gaps using SQL? I think I need some logic like
INSERT INTO BALANCES (BalanceDate,BalanceAmount)
VALUES(previous record + 1 day, previous record's amount)
but I don't know how I can point SQL to the previous record.
Thanks
You can use a recursive cte to produce the missing dates:
WITH cte AS (
SELECT date(b1.BalanceDate, '+1 day') BalanceDate, b1.Amount
FROM BALANCES b1
WHERE NOT EXISTS (SELECT 1 FROM BALANCES b2 WHERE b2.BalanceDate = date(b1.BalanceDate, '+1 day'))
AND date(b1.BalanceDate, '+1 day') < (SELECT MAX(BalanceDate) FROM BALANCES)
UNION ALL
SELECT date(c.BalanceDate, '+1 day'), c.Amount
FROM cte c
WHERE NOT EXISTS (SELECT 1 FROM BALANCES b WHERE b.BalanceDate = date(c.BalanceDate, '+1 day'))
AND date(c.BalanceDate, '+1 day') < (SELECT MAX(BalanceDate) FROM BALANCES)
)
INSERT INTO BALANCES(BalanceDate, Amount)
SELECT BalanceDate, Amount FROM cte;
See the demo.
I have two seperate columns for date and time each being saved in varchar2
I'm trying to query a specific range of time:
i.e. 1/1/2017 - 1/31/2017
between 6PM-6AM each day
So far I did this:
select * from (select a.*,TO_DATE(billdate||' '||billtime,'YYYY/MM/DD HH24:Mi:SS')
as Timex from billtable a where billdate >= '2017/01/01' and billdate <= '2017/01/31')
where timex>=to_date(''2017/01/01 18:00:00','YYYY/MM/DD HH24:Mi:SS')
and timex<=to_date('2017/01/31 06:00:00','YYYY/MM/DD HH24:Mi:SS')
order by billdate
What can I do further or Is It the wrong way Iam going?
Thanks!
Assuming you're stuck with the data model you have (storing dates and/or times as strings, or separately, is not a good idea) and that you are not interested in the six hours before and after the date range, the formats you've used at least allow you to query those ranges fairly simply:
select a.*, to_date(billdate||' '||billtime,'YYYY/MM/DD HH24:Mi:SS') as timex
from billtable a
where billdate >= '2017/01/01'
and billdate <= '2017/01/31'
and (billtime <= '06:00:00' or billtime >= '18:00:00')
order by billdate, billtime;
With some sample data provided in a CTE:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
with billtable (billdate, billtime) as (
select '2017/01/01', '00:00:00' from dual
union all select '2017/01/01', '06:00:00' from dual
union all select '2017/01/01', '06:00:01' from dual
union all select '2017/01/31', '17:59:59' from dual
union all select '2017/01/31', '18:00:00' from dual
union all select '2017/01/31', '23:59:59' from dual
)
select a.*, to_date(billdate||' '||billtime,'YYYY/MM/DD HH24:Mi:SS') as timex
from billtable a
where billdate >= '2017/01/01'
and billdate <= '2017/01/31'
and (billtime <= '06:00:00' or billtime >= '18:00:00')
order by billdate, billtime;
BILLDATE BILLTIME TIMEX
---------- -------- -------------------
2017/01/01 00:00:00 2017-01-01 00:00:00
2017/01/01 06:00:00 2017-01-01 06:00:00
2017/01/31 18:00:00 2017-01-31 18:00:00
2017/01/31 23:59:59 2017-01-31 23:59:59
If you already had a date, or were converting to a date - or in fact a timestamp to make this work - you could do:
select billdate, billtime, cast(timex as date)
from (
select a.*, to_timestamp(billdate||' '||billtime,'YYYY/MM/DD HH24:Mi:SS') as timex
from billtable a
where billdate >= '2017/01/01' and billdate <= '2017/01/31'
)
where extract(hour from timex) < 6
or (extract(hour from timex) = 6 and extract(minute from timex) = 0 and extract(second from timex) = 0)
or extract(hour from timex) >= 18
order by timex;
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