Compute sum of hours/day with overlapping periods, SQL - sql

I have a table with the start date(event.startdate), end date(event.enddate), and the hours/person (event.hrday) of an event. I have another table with weekdays listed which has another field for each person (calendar.name). I want to populate those columns with the total hours worked each day. I can't seem to figure out how to properly sum the hours if two events overlap in dates, I can only come up with a correct value for a single event in a time period.
I believe that in theory this question has the answer I need: compute sum of values associated with overlapping date ranges
But I am very new to SQL and I don't fundamentally understand the solution posted even after some additional research. I am using Access 2013. Apologies if this is a super elementary question, I was hoping what I wanted to do could be handled "visually" with Access...
What I Have: ("event" table)
Startdate | Enddate | Hrsday | Name
5/1/2015 5/12/2015 1.25 Joe
5/7/2015 5/8/2015 8 Joe
What I'm looking for:("calendar" table, days already filled in first column)
Weekdays | Joe | name2 | name3 | ....
5/1/2015 1.25
5/4/2015 1.25
5/5/2015 1.25
5/6/2015 1.25
5/7/2015 9.25
5/8/2015 9.25
5/11/2015 1.25
5/12/2015 1.25
I've tried using the query builder within access to build an UPDATE query, but my result either does not appear at all (no updates, all null) or will only fill in one event with no overlaps. (5/1-5/12 all have 1.25).

I think you will need to create a "date table" if you want to be able to achieve this sort of result in MS-Access (without using windowed functions).
Here is a quick example of how this might work in SQL Server, but only using syntax available to MS-Access (I hope).
--Load the test data into a table variable
DECLARE #event TABLE (
[start_date] DATE,
end_date DATE,
hrsperday NUMERIC(19,2),
name VARCHAR(20));
INSERT INTO #event SELECT '20150401', '20150412', 1.25, 'Joe';
INSERT INTO #event SELECT '20150407', '20150408', 8, 'Joe';
--Add some more test data, to make it more "interesting"
INSERT INTO #event SELECT '20150401', '20150405', 0.1, 'Bill';
INSERT INTO #event SELECT '20150401', '20150430', 7.5, 'Bill';
INSERT INTO #event SELECT '20150412', '20150415', 0.5, 'Bill';
--Make a date table, this creates one on the fly but wouldn't work in MS-Access
--I store a date for each day in 2015/Apr, obviously I would want more dates eventually
DECLARE #dates TABLE (
[date] DATE);
WITH cte AS (
SELECT CONVERT(DATE, '20150401') AS [date]
UNION ALL
SELECT DATEADD(DAY, 1, [date]) FROM cte WHERE [date] < '20150430')
INSERT INTO
#dates
SELECT
[date]
FROM
cte OPTION (MAXRECURSION 0);
--Now the answer is trivial
SELECT
e.name,
d.[date],
SUM(hrsperday) AS hrs
FROM
#dates d
LEFT JOIN #event e ON d.[date] BETWEEN e.[start_date] AND e.end_date
GROUP BY
e.name,
d.[date]
ORDER BY
e.name,
d.[date];
--Note the format you want, but a PIVOT would give you this
--(I don't think PIVOT is supported by MS-Access though)
Results for this are:
name date hrs
Bill 2015-04-01 7.60
Bill 2015-04-02 7.60
Bill 2015-04-03 7.60
Bill 2015-04-04 7.60
Bill 2015-04-05 7.60
Bill 2015-04-06 7.50
Bill 2015-04-07 7.50
Bill 2015-04-08 7.50
Bill 2015-04-09 7.50
Bill 2015-04-10 7.50
Bill 2015-04-11 7.50
Bill 2015-04-12 8.00
Bill 2015-04-13 8.00
Bill 2015-04-14 8.00
Bill 2015-04-15 8.00
Bill 2015-04-16 7.50
Bill 2015-04-17 7.50
Bill 2015-04-18 7.50
Bill 2015-04-19 7.50
Bill 2015-04-20 7.50
Bill 2015-04-21 7.50
Bill 2015-04-22 7.50
Bill 2015-04-23 7.50
Bill 2015-04-24 7.50
Bill 2015-04-25 7.50
Bill 2015-04-26 7.50
Bill 2015-04-27 7.50
Bill 2015-04-28 7.50
Bill 2015-04-29 7.50
Bill 2015-04-30 7.50
Joe 2015-04-01 1.25
Joe 2015-04-02 1.25
Joe 2015-04-03 1.25
Joe 2015-04-04 1.25
Joe 2015-04-05 1.25
Joe 2015-04-06 1.25
Joe 2015-04-07 9.25
Joe 2015-04-08 9.25
Joe 2015-04-09 1.25
Joe 2015-04-10 1.25
Joe 2015-04-11 1.25
Joe 2015-04-12 1.25

Related

Access SQL Query to Count Unique Occurrences of One Field Matching Multiple Parameters/Rows, Some Identical

Struggling with ms-access's flavor of SQL queries still, though I've made some progress (thanks to y'all). I have an event log table like this:
Logs Table
logID (auto#)
modID (str)
relID (str)
DateTime (date)
TxType (short)
1
1234
22.3
10/1/22 0800
6
2
1234
22.3
10/1/22 0900
7
3
1234
22.3
10/1/22 1000
13
4
1234
22.3
10/1/22 1100
15
5
4321
22.3
10/1/22 0830
1
6
4321
22.3
10/1/22 0930
13
7
4321
22.3
10/1/22 1030
15
8
4321
22.3
10/1/22 1130
13
9
1234
23.1
11/1/22 0800
1
10
1234
23.1
11/1/22 0900
15
11
1234
23.1
11/1/22 1000
13
12
1234
23.1
11/1/22 1100
15
13
4321
23.1
11/1/22 0830
13
14
4321
23.1
11/1/22 0930
7
15
4321
23.1
11/1/22 1030
13
16
4321
23.1
11/1/22 1130
15
What I need to do is:
filter the table by relID, then
count the number of modID's that have a 15 txType as the last/most recent chronological event in their rows.
So ideally I'd filter e.g. by relID=23.1 and get these results (but not logID # 10 for example) and then count them:
logID (auto#)
modID (str)
relID (str)
DateTime (date)
TxType (short)
12
1234
23.1
11/1/22 1100
15
16
4321
23.1
11/1/22 1130
15
As part of another function I have been able to count any modID's having a single txType successfully using
SELECT COUNT(*)
FROM (
SELECT DISTINCT Logs.modID, Logs.relID
FROM Logs
WHERE ((Logs.relID='23.1') AND ((Logs.TxType=13)))
);
Another stackoverflow user (exception - thanks!) showed me how to get the last event type for a given modID, relID combination using
SELECT TOP 1 TxType
FROM Logs
WHERE (((Logs.modID=[EnterModID])) AND ((Logs.relID=[EnterRelID])))
ORDER BY DateTime DESC;
But I'm having trouble combining these two. I know I can combine COUNT and GROUP BY but Access treats GROUP BY very particularly, and I'm not sure how to use SELECT TOP to get the latest events for each modID rather than just the latest events in the table, period.
This should give you the logID from the row with the latest DateTime for each combination of modIDand your target relID:
PARAMETERS which_relID Text(255);
SELECT DISTINCT
(
SELECT TOP 1 logID
FROM Logs
WHERE modID=l.modID AND relID=l.relID
ORDER BY [DateTime] DESC
) AS latest_modID
FROM Logs AS l
WHERE l.relID=[which_relID]
Use it as a subquery which you INNER JOIN to your Logs table. Note the subquery evaluates rows regardless of TxType. So have the parent query select only rows whose TxType = 15
PARAMETERS which_relID Text(255);
SELECT l2.*
FROM
Logs AS l2
INNER JOIN
(
SELECT DISTINCT
(
SELECT TOP 1 logID
FROM Logs
WHERE modID=l.modID AND relID=l.relID
ORDER BY [DateTime] DESC
) AS latest_modID
FROM Logs AS l
WHERE l.relID=[which_relID]
) AS sub
ON l2.logID=sub.latest_modID
WHERE l2.TxType=15;
Note I moved the PARAMETERS clause into the parent query. But you can eliminate it altogether if you believe it's causing trouble.
DateTime is a reserved word. I enclosed it in square brackets to ensure Access understands we mean the name of an object.
Using your sample data, I get these 2 rows when I supply 23.1 for the query parameter:
logID
modID
relID
DateTime
TxType
12
1234
23.1
11/1/2022 11:00:00 AM
15
16
4321
23.1
11/1/2022 11:30:00 AM
15
I get a single row with 22.3 for the parameter:
logID
modID
relID
DateTime
TxType
4
1234
22.3
10/1/2022 11:00:00 AM
15

Display the Names and Date of Births of all Programmers Born in January

I am trying to use datetime to find the date of births (DOB) during January of certain PName. I have a short part that works in SQL Server, but I would like to find out the DOB of Jan or 1st month. It works in SQL Server for the whole database, but when I want to find just the particular month (DOB) of the database I can't find a correct way to right it. According to the Internet the Oracle it was written:
SELECT DOB, NAME FROM PROGRAMMER WHERE TO_CHAR(DOB,'MON') LIKE 'JAN';
I am trying to find the SQL Server - Programmer with DOB - say January
Name and dates of DOB with Jan
SELECT TOP (1000) [Programmer_Id]
, [PName]
**, [DOB]**
, [DOB2]
, [DOJ]
, [DOJ2]
, [Gender]
, [Prof1]
, [Prof2]
, [Salary]
, [Salary2]
FROM [Studies_S_P_no_foreign].[dbo].[Programmer]
SELECT
DATEPART(month, DOB) [month] --#d2 DOB '2019-01-01 14:30:14'
--where DOB is like 1
From Programmer
[Programmer_Id],[PName], [DOB], [DOB2], [DOJ], [DOJ2], [Gender], [Prof1], [Prof2], [Salary], [Salary2]
1 ANAND 1966-04-12 00:00:00.000 1966-04-01 1992-04-21 00:00:00.000 1992-04-21 M PASCAL BASIC 3200 3200.00
2 ALTAF 1964-07-02 00:00:00.000 1964-07-02 1990-11-13 00:00:00.000 1990-11-13 M CLIPPER COBOL 2800 2800.00
3 JULIANA 1960-01-31 00:00:00.000 1960-01-31 1990-04-21 00:00:00.000 1990-04-21 F COBOL DBASE 3000 3000.00
4 KAMALA 1968-10-30 00:00:00.000 1968-10-30 1992-01-02 00:00:00.000 1992-01-02 F C DBASE 2900 2900.00
5 MARY 1970-06-24 00:00:00.000 1970-06-24 1991-02-01 00:00:00.000 1991-02-01 F CPP ORACLE 4500 4500.00
6 NELSON 1985-09-11 00:00:00.000 1985-09-11 1989-10-11 00:00:00.000 1989-10-11 M COBOL DBASE 2500 2500.00
7 PATTRICK 1965-11-10 00:00:00.000 1965-11-10 1990-04-21 00:00:00.000 1990-04-21 M PASCAL CLIPPER 2800 2800.00
8 QADIR 1965-08-31 00:00:00.000 1965-08-31 1991-04-21 00:00:00.000 1991-04-21 M ASSEMBLY C 3000 3000.00
9 RAMESH 1967-05-03 00:00:00.000 1967-05-03 1991-02-28 00:00:00.000 1991-02-28 M PASCAL DBASE 3200 3200.00
10 REBECCA 1967-01-01 00:00:00.000 1967-01-01 1990-12-01 00:00:00.000 1990-12-01 F BASIC COBOL 2500 2500.00
11 REMITHA 1970-04-19 00:00:00.000 1970-04-19 1993-04-20 00:00:00.000 1993-04-20 F C ASSEMBLY 3600 3600.00
12 REVATHI 1969-12-02 00:00:00.000 1969-12-02 1992-01-02 00:00:00.000 1992-01-02 F PASCAL BASIC 3700 3700.00
13 VIJAYA 1965-12-14 00:00:00.000 1965-12-14 1992-05-02 00:00:00.000 1992-05-02 F FOXPRO C 3500 3500.00
NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
There are many ways you can do the comparison in SQL Server. Below are some of them.
DECLARE #table table(DOB DATETIME)
INSERT INTO #table
values ('2019-01-01 14:30:14')
SELECT * FROM #table where MONTH(DOB) = 1
SELECT * FROM #table where DATENAME(mm,dob) = 'January'
SELECT * FROM #table where DATEPART(mm,dob) = 1
If want to do similar to Oracle for 3 character month
SELECT * FROM #table where LEFT(DATENAME(mm,dob),3) = 'JAN'
CAVEAT Thanks to #AlwaysLearning
datename is set language-sensitive. e.g.: for January, French=janvier;
Italian=gennaio; German=Januar; Spanish=Enero. If you have a
multicultural database prefer to use month or datepart

MS SQL Server GETDATE - 90 where -90 not like previous

Today for me is 2017-08-02 13:00:00.000 and I have this table :
TABLE1
TIME NAME PRODUCT
2014-10-10 08:34:57.000 Bob Glu
2015-11-03 13:34:27.000 Bob Finger
2017-07-08 09:51:46.000 Bob Note
2017-07-08 09:51:46.000 Bob PC
2017-07-31 09:51:46.000 Bob Car
2017-07-31 09:51:46.000 Bob Ball
2017-07-31 09:51:46.000 Bob Shoe
2017-04-01 08:34:57.000 Alex Pen
2017-06-03 13:34:27.000 Alex Horse
2017-07-31 05:51:46.000 Alex Bread
2017-07-31 09:51:46.000 Alex Hummus
2017-07-31 09:51:46.000 Alex Kitchen
2017-07-31 09:51:46.000 Alex Hell
2017-07-31 09:51:46.000 Alex Night
2016-10-10 08:34:57.000 Eva Mouse
2017-05-03 13:34:27.000 Eva Cement
2017-06-31 06:51:46.000 Eva Pc
2017-06-31 03:51:46.000 Eva Bread
2017-06-31 08:51:46.000 Eva Shoe
2017-06-31 07:51:46.000 Eva Ball
2017-06-31 07:51:46.000 Eva Zoo
I need to know which PRODUCT was lost from the above table, but I need to know only PRODUCT which was lost from last (TIME + NAME) and – 90 days. I expect results like this:
TABLE1
TIME NAME Product
2017-05-03 13:34:27.000 Eva Cement
2017-06-03 13:34:27.000 Alex Horse
2017-07-08 09:51:46.000 Bob Note
2017-07-08 09:51:46.000 Bob PC
The table has millions of names and millions of products. At the moment I am interest for 90 days from the last updates.
I am trying
SELECT * from table1
WHERE (TIME > getdate ()- 90) AND (TIME not like getdate ()- 90)
Try where DATEDIFF(day, TIME, GETDATE()) < 90
are you trying to figure out the first thing each person lost in the past 90 days? and include all ties?
Lets limit data and add a window function in a cte and then limit the data to just #1's
;with cte as
(
SELECT * , DENSE_RANK() over (partition by [name] order by [time]) as DR
from table1
WHERE [time] >= dateadd('d',-90,getdate())
)
select *
from cte
where dr=1
If you want rows from the previous 90 days from today.
select * from table1
where TIME >= getdate()-90

Select first and last occurrence of repeated colums in sql server

I have a table like this,
Date is in yyyy-mm-dd format
Name Date Credits
--------------------------------
Bill 2013-04-04 5
Paul 2013-04-05 4
Bill 2013-04-05 3
Angel 2013-04-07 9
Bill 2013-05-01 8
Paul 2013-05-02 7
Bill 2013-06-15 6
Angel 2013-07-22 15
Paul 2013-07-23 7
Angel 2013-08-11 9
And my expected result is
Name MinDate MaxDate Credits
-----------------------------------------------
Bill 2013-04-04 2013-06-15 1
Paul 2013-04-05 2013-07-23 3
Angel 2013-04-07 2013-08-11 0
How to form the Query. Help needed.
My approach would be something like this:
SELECT t1.name, MIN(t1.date) AS MinDate, MAX(t1.date) AS MaxDate
FROM table t1
GROUP BY t1.name
I don't know how you calculate your credits, though, so I left this one out.
If it's SUM(t1.credit) or something alike, just add this to the FROM-clause.
Hope this helps.

Incremental count in SQL Server 2005

I am working with a Raiser's Edge database using SQL Server 2005. I have written SQL that will produce a temporary table containing details of direct debit instalments. Below is a small table containing the key variables for the question I'm going to ask, with some fictional data:
Donor_ID Instalment_ID Instalment_Date Amount
1234 1111 01/01/2011 £5.00
1234 1112 01/02/2011 £0.00
1234 1113 01/03/2011 £5.00
1234 1114 01/04/2011 £5.00
1234 1115 01/05/2011 £0.00
1234 1116 01/06/2011 £0.00
2345 2111 01/01/2011 £0.00
2345 2112 01/02/2011 £5.00
2345 2113 01/03/2011 £5.00
2345 2114 01/04/2011 £0.00
2345 2115 01/05/2011 £0.00
2345 2116 01/06/2011 £0.00
As you will see, some of the values in the Amount column are £0.00. This can occur when a donor has insufficient funds in their account, for example.
What I'd like to do is write a SQL query that will create a field containing an incremental count of consecutive £0.00 payments that resets after a non-£0.00 payment or after a change in Donor_ID. I have reproduced the above data below, with the field I'd like to see.
Donor_ID Instalment_ID Instalment_Date Amount New_Field
1234 1111 01/01/2011 £5.00
1234 1112 01/02/2011 £0.00 1
1234 1113 01/03/2011 £5.00
1234 1114 01/04/2011 £5.00
1234 1115 01/05/2011 £0.00 1
1234 1116 01/06/2011 £0.00 2
2345 2111 01/01/2011 £0.00 1
2345 2112 01/02/2011 £5.00
2345 2113 01/03/2011 £5.00
2345 2114 01/04/2011 £0.00 1
2345 2115 01/05/2011 £0.00 2
2345 2116 01/06/2011 £0.00 3
To help clarify what I'm looking for, I think what I'm looking to do would be similar to a winning streak field on a list of a football team's results. For example:
Opponent Score Winning_Streak
Arsenal 1-0 1
Liverpool 0-0
Swansea 3-1 1
Chelsea 2-1 2
Fulham 4-0 3
Stoke 0-0
Man Utd 1-3
Reading 2-1 1
I've considered various options, but have made no progress. Unless I've missed something obvious, I think that a solution more advanced than my current SQL programming level might be required.
If I am thinking about this problem correctly, I believe that you want a row number when the Amount is 0.00 pounds.
Select 0 as As InsufficientCount
, Donor_ID
, Installment_ID
, Amount
From [Table]
Where Amount > 0.00
Union
Select Row_Number() Over (Partition By Donor_ID Order By Installment_ID)
, Donor_ID
, Installment_ID
, Amount
From [Table]
Where Amount = 0.00
This union select should only give you 'ranks' where the Amount equals 0.
Am calling your new field streakAmount
ALTER TABLE instalments ADD streakAmount int NULL;
Then, to update the value:
UPDATE instalments
SET streakAmount =
(SELECT
COUNT(*)
FROM
instalments streak
WHERE
streak.donor_id = instalments.donor_id
AND
streak.instalment_date <= instalments.instalment_date
AND
(streak.instalment_date >
-- find previous instalment date, if any exists
COALESCE(
(
SELECT
MAX(instalment_date)
FROM
instalments prev
WHERE
prev.donor_id = instalments.donor_id
AND
prev.amount > 0
AND
prev.instalment_date < instalments.instalment_date
)
-- otherwise min date
, cast('1753-1-1' AS date))
)
)
WHERE
amount = 0;
http://sqlfiddle.com/#!6/a571f/18