Comparing data between reporting periods MS Access - sql

I have a big table that contains records for each reporting period in my project.
The period is indentified by an integer 1, 2, 3, 4, 5, 6, 7.
Each period contains about 7000 rows of tasks each identified a unique ID. These tasks all have a percent complete column which is an integer.
I want to add a comparison column.
so it would look up the unique id in the previous period then return the difference in percent complete.
eg
for
Period: 8 Item Unique ID: 42w3wer324wer32 Percent complete: 50
it would find:
Period: 7 Item Unique ID: 42w3wer324wer32 Percent complete: 40
the fill in the field with: 10.
If it could not find the Item Unique ID in the previous period then it would default to 0.
thanks

As I understand your description, you could pull the data for period 8 like this:
SELECT item_id, pct_complete
FROM YourTable
WHERE rpt_period = 8;
And the previous period would be the same query except substituting 7 as the period.
So take the period 8 query and left join it to a subquery for period 7.
SELECT
y.item_id,
(y.pct_complete - Nz(sub.pct_complete, 0)) AS change_in_pct_complete
FROM YourTable AS y
LEFT JOIN
(
SELECT item_id, pct_complete
FROM YourTable
WHERE rpt_period = 7
) AS sub
ON y.item_id = sub.item_id
WHERE rpt_period = 8;
That Nz() expression will substitute 0 for Null when no period 7 match exists for a period 8 item_id.
If you need a query which will not be running from within an Access session, the Nz() function will not be available. In that case, you can use an IIf() expression ... it's not as concise, but it will get the job done.
IIf(sub.pct_complete Is Null, 0, sub.pct_complete)

Related

Get the 3rd largest time value in SQL Server 2018

I want to get the 1st, 2nd, 3rd,...largest value of Time in SQL Server 2018's table.
In my code when I get the 3rd largest, if my month condition is 5, it returns correct value. But if my month condition is 4, it returns the max time value not my expected time value ( 3rd ). I don't know why.
My query:
SELECT MAX([Time1].[Time]) FROM [dbo].[index4_MonthChart] as [Time1]
WHERE
Month([Time]) = '4'
AND [Time1].[Time] < ( SELECT MAX([Time2].[Time]) FROM [dbo].[index4_MonthChart] as [Time2]
WHERE
[Time2].[Time] < ( SELECT MAX([Time3].[Time]) FROM [dbo].[index4_MonthChart] as [Time3]))
All my value in table:
When month(time) condition is 5, it returns:
But when month(time) is 4, it always returns the max time value and it is wrong ( The correct time value have to be 2023-04-28 ) :
Very simple. You need to add a where clause in your sub-selects to restrict those to Month= 4 as well. Since there are at least two times greater than 30.04 (IE all the times from May), your current query returns the maximum time for April. This doesn't happen for May, because your data has no values later than May.

How to run a query for multiple independent date ranges?

I would like to run the below query that looks like this for week 1:
Select week(datetime), count(customer_call) from table where week(datetime) = 1 and week(orderdatetime) < 7
... but for weeks 2, 3, 4, 5 and 6 all in one query and with the 'week(orderdatetime)' to still be for the 6 weeks following the week(datetime) value.
This means that for 'week(datetime) = 2', 'week(orderdatetime)' would be between 2 and 7 and so on.
'datetime' is a datetime field denoting registration.
'customer_call' is a datetime field denoting when they called.
'orderdatetime' is a datetime field denoting when they ordered.
Thanks!
I think you want group by:
Select week(datetime), count(customer_call)
from table
where week(datetime) = 1 and week(orderdatetime) < 7
group by week(datetime);
I would also point out that week doesn't take the year into account, so you might want to include that in the group by or in a where filter.
EDIT:
If you want 6 weeks of cumulative counts, then use:
Select week(datetime), count(customer_call),
sum(count(customer_call)) over (order by week(datetime)
rows between 5 preceding and current row) as running_sum_6
from table
group by week(datetime);
Note: If you want to filter this to particular weeks, then make this a subquery and filter in the outer query.

How to INSERT with cumulative add from a previous row?

In the following table
----------------------------
| id | day | count |
----------------------------
1 2013-01-01 10
1 2013-01-05 20
1 2013-01-08 45
the second and third row the count column is cumulative i.e. 20 = (10 from first row + 10 additional count) and 45 ( 20 from second row + 25 additional count). How can the second and third rows (and further) be inserted with cumulative add in Postgresql ?
Note: The additional count is read from a variable in a program. So the aim is to store this value in the 'count' column in Postgresql, but also add it with the 'count' found by last entry in ascending date order.
Since you don't say where does the additional count come from I assume there is an additional count column:
select *,
sum(additional_count) over(order by "day") "count"
from t
order by "day"
The sum function as a window function does a running total. It is a window function when it uses the over clause.
If the problem is how the insert statement with a select could look like:
insert into x(id, day, count)
select 1, current_timestamp,
coalesce((select max(count) from x), 0) + 10;
But this is not necessarily the best way to solve the problem.

Obtaining one value per person from table ("latest-n-per-group before cutoff date")

In a database of mine there is a table called 'Budget' defined as follows
CREATE TABLE BUDGET (
ID INTEGER NOT NULL,
THERAPIST INTEGER,
CURDATE DATE,
ILLNESS SMALLINT,
HOLIDAY SMALLINT);
ALTER TABLE BUDGET ADD PRIMARY KEY (ID);
The purpose of the table is to store how many hours a month a therapist is entitled to sick leave and regular holiday.
The table has the following values
ID: 1, THERAPIST: 36, CURDATE: 01/01/2012, ILLNESS:8, HOLIDAY: 8
ID: 2, THERAPIST: 36, CURDATE: 01/07/2012, ILLNESS:8, HOLIDAY: 10
ID: 3, THERAPIST: 74, CURDATE: 01/01/2012, ILLNESS:8, HOLIDAY: 8
ID: 4, THERAPIST: 74, CURDATE: 01/03/2012, ILLNESS:8, HOLIDAY: 10
I want to write a query which returns one row per therapist, with the most recent data (the above data shows that therapist 36 was entitled to 8 hours holiday per month in the months 1-6/2012, and from 7/2012 she is entitled to 10 hours per month). In other words, if I issue the query for the date 31/01/2012, I expect to get back rows 1 and 3; if I issue the query for the date 31/05/2012, I expect to get back rows 1 and 4 and if I issue the query for the date 31/08/2012, I expect to get back rows 2 and 4.
The following query gives me one row per therapist, but it gives me the maximum values of illness and holiday, which is not necessarily what I want.
select therapist, max (illness), max (holiday)
from budget
where curdate <= '2012-08-31'
group by therapist
Should there be a fifth row in the table
ID: 5, THERAPIST: 74, CURDATE: 01/07/2012, ILLNESS: 6, HOLIDAY: 6
querying on 31/08/12 would return illness=8 and holiday=10, even though they should both be 6.
I tried the following query but this returns only one row
select therapist, illness, holiday
from budget
where curdate =
(select max (curdate) from budget b
where b.therapist = budget.therapist
and b.curdate <= '2012-08-31')
There are similar questions on SO, but none of them seem applicable to my situation. Of course, life would be easier if I could use 'group by' without an aggregate function but Firebird (and most DBMS) doesn't support this.
If I understood your problem correctly, I think you want:
select
b.*
from
budget b
join (
select
therapist,
max(curdate) as maxdate
from
budget
where
curdate <= '2012-08-31'
group by
therapist
) grouped on grouped.therapist = b.therapist and grouped.maxdate = b.curdate
This is essentially a de-duplication problem, as in you would have the same problem if you were trying to clean up your data such that there was only one row per therapist for the criteria you described. This is rarely simple.
That said, here is something you could use in your current situation:
select b.id, b.therapist, t.[curdate], b.illness, b.holiday
from budget b
inner join
(
select therapist, MAX([curdate]) as [curdate]
from BUDGET
where [CURDATE] <= '2012-08-31'
group by THERAPIST
) t on b.therapist = t.therapist and b.[CURDATE] = t.[curdate]
I think it would be something like this:
select * from therapist t
join budget a
on t.therapist=a.therapist
where id in (
select first 1 ID from BUDGET b
where
b.theraphist=t.therapist and
b.curdate <= '2012-08-31'
order by curdate desc)

SQL server datetimeoffset data aggregation

I am currently developing an application that makes use of an SQL database. In this database I maintain a table called session, which has three fields: a session id (int), a date_created (datetimeoffset), and a date_expired (datetimeoffset) field.
I want to group my sessions in clusters of sessions that the minimum date_created and the maximum date_expired of the sessions not to be more than 6 hours. Also, I don't want my groups to overlap, i.e. If session s1 belongs to group 1, I do not want it to be also in group 2.
Any ideas?
I suggest you create your 4 data groups like 0-6, 6-12, 12-18 and 18-24, so you can do like this:
FYI: for the sake of simplicity, I did the case on the date column only, you will need to use a datediff between your date_created and date_expired
FYI2: change the values on the between as it suits you better and in the end the query will return values to 1, 2, 3 and 4, which you should change to "0 to 6", "6 to 12" and so one..
with MyCTE as (
select case
when datepart(hh,date ) between 0 and 6 then 1
when datepart(hh,date ) between 6 and 12 then 2
when datepart(hh,date ) between 13 and 18 then 3
else 4
end
as myDatebucket
, *
from session
)
select myDatebucket, count(*)
from MyCTE
group by myDatebucket
order by myDatebucket