Count instances of consecutive dates for associated name (VBA, SQL) - sql

Good morning all,
I am trying to determine instances of consecutive dates (excluding Sunday) from a data set. The data is stored in Access and I am pulling the required dates into Excel. I am then trying to determine how many instances each person has in the data provided. Example below.
Data example:
| Name | Date of absence|
| Bob | 02/01/17 |
| Jill | 02/01/17 |
| Bob | 03/01/17 |
| Jill | 04/01/17 |
Result example:
Bob - 1 Instance, 2 days
Jill - 2 Instance, 2 days
I started trying to work through this with VBA in Excel using loops to rotate through each instance of absence until all people had been completed/ticked off, however the code was becoming really cumbersome and it felt very inefficient, not to mention how slow it was getting for larger data sets! I wonder if it is possible to query the database for the info or to write something a bit more efficient.
Any help or suggestions would be appreciated!
Update:
Testing Tom's suggestion;
Sql = "SELECT Absence.Racf,count(RecordDate) as dups"
Sql = Sql & " FROM Absence"
Sql = Sql & " left outer join"
Sql = Sql & " (select Racf, [RecordDate]+IIf(Weekday([RecordDate],7)=1,2,1) as date1 from Absence) t1"
Sql = Sql & " on Absence.RecordDate=t1.date1 and Absence.Racf=t1.Racf"
Sql = Sql & " where date1 Is Not Null"
Sql = Sql & " group by Absence.Racf"
But unfortunately on the list of dates below it returns 7, instead of 5.
Dates:
23-Feb-16,24-Feb-16,08-Aug-16,09-Aug-16,10-Aug-16,31-Aug-16,24-Oct-16,25-Oct-16,26-Oct-16,25-Jan-17,26-Jan-17,27-Jan-17

So this is how the SQL might actually look in an Access query
SELECT table1.name,count(date) as dups
FROM Table1
left outer join
(select name, [date]+IIf(Weekday([Date],7)=1,2,1) as date1 from table1) t1
on table1.date=t1.date1 and table1.name=t1.name
where date1 is not null
group by table1.name
;
If you want to run this from Excel using a macro, here is a useful reference.
I lifted the code from there and changed the lines which set up the SQL query string to
SQL = "SELECT table1.name,count(date) as dups"
SQL = SQL & " FROM table1"
SQL = SQL & " left outer join"
SQL = SQL & " (select name, [date]+IIf(Weekday([Date],7)=1,2,1) as date1 from table1) t1"
SQL = SQL & " on table1.date=t1.date1 and table1.name=t1.name"
SQL = SQL & " where date1 Is Not Null"
SQL = SQL & " group by table1.name"
and it worked fine.
Try this if you want to get sequences with length greater than one
SELECT Absence.Racf, Count(Absence.RecordDate) AS CountOfRecordDate
FROM (Absence LEFT JOIN (select Racf, RecordDate+IIf(Weekday([RecordDate],7)=1,2,1) as RecordDate1 from Absence) AS t1 ON (Absence.RecordDate = t1.RecordDate1) AND (Absence.Racf = t1.Racf))
LEFT JOIN (select Racf, [RecordDate]-IIf(Weekday([RecordDate],2)=1,2,1) as RecordDate2 from Absence) AS t2 ON (Absence.RecordDate = t2.RecordDate2) AND (Absence.Racf = t2.Racf)
WHERE (((t1.RecordDate1) Is Not Null) AND ((t2.RecordDate2) Is Null))
GROUP BY Absence.Racf;
Or this if you want to get sequences of one or more consecutive dates
SELECT Absence.Racf, Count(Absence.RecordDate) AS CountOfRecordDate
FROM Absence LEFT JOIN (select Racf, [RecordDate]+IIf(Weekday([RecordDate],7)=1,2,1) as RecordDate2 from Absence) AS t2 ON (Absence.RecordDate = t2.RecordDate2) AND (Absence.Racf = t2.Racf)
WHERE (((t2.RecordDate2) Is Null))
GROUP BY Absence.Racf;
adding to the SQL string as before.

This can be done using array formula in Excel. In D I have =INDEX($A2:$A$15,MATCH(0,COUNTIF($D$1:$D1,$A2:$A$15),0)) to get the unique employees, then in E I have the following to count the instances =SUM(--(($A$1:$A$15=D1)*(OFFSET($A$1:$A$15,1,0)=D1)*(OFFSET($B$1:$B$15,1,0)-$B$1:$B$15)=1)) which gives the result something like this. You'll need to add another criteria, based on weekday (I will adjust a little later as running low on time) This relies on the data being in date order
EDIT : I understand this is not the full answer and will require modification, a starting point :o)
Covering the Sunday absence (will still need weekday check):
=D1 & " " & COUNTIF($A$1:$A$15,D1) &" instances " & SUM(--(--($A$1:$A$15=D1)*--(OFFSET($A$1:$A$15,1,0)=D1))*--(--(OFFSET($B$1:$B$15,1,0)-$B$1:$B$15=1)+--(OFFSET($B$1:$B$15,1,0)-$B$1:$B$15=2)))&" Consecutive"
Checking the weekday also
=D2 & " " & COUNTIF($A$1:$A$15,D2) &" instances " & SUM(--(--($A$1:$A$15=D2)*--(OFFSET($A$1:$A$15,1,0)=D2))*--(--(OFFSET($B$1:$B$15,1,0)-$B$1:$B$15=1)+--(WEEKDAY(OFFSET($B$1:$B$15,1,0),2)=1)*((OFFSET($B$1:$B$15,1,0)-$B$1:$B$15=2)))) & " Consecutive"

A SQL approach would be something along the lines of, based on a table 000Absence, which is the data from examples EEName and AbsDate.
SELECT abs1.EEName, abs1.AbsDate,
(select count(abs2.EEName) from 000Absence as abs2 where abs2.[EEName]=abs1.[EEName]) AS INSTANCES,
(select count(abs3.EEName) from 000Absence as abs3 where abs3.[EEName]=abs1.[EEName] and abs3.[AbsDate]=abs1.[AbsDate]+iif(weekday(abs3.[AbsDate],7)=1,2,1)) AS CONSECUTIVE
FROM 000Absence AS abs1;
Where the output can be got from the query, grouping by Employee etc.

Related

Dynamic Selection Query with Criteria in Ms Access | SQL

I have a query in which I select multiple values. To create my form.
The results look like this :
the last Column [Candidat Nom] is a drop down list with an other query (lets call it Query 1) that select my drop down list values.
The selection is good and is what I'm looking for. Except I get the same value for all lines when they need to be different.
To simplify
let's take the following exemple
I have the following candidate that wants to join for the following job (Represented with their ID).
As we can see 2 candidates wants job N° 12. and 1 candidate for each other job.
What I get
All candidates are listed for every job.
What I want
What I actually did
is I put the following query (Query 1) on my column.
SELECT T_SALARIE.SALARIE_nom & " " & T_SALARIE.SALARIE_prenom AS Candidat Nom
FROM T_EMPLOI INNER JOIN (T_SALARIE INNER JOIN (T_SALARIE_EMPLOI LEFT JOIN T_STATUT_EMPLOI ON T_SALARIE_EMPLOI.SALARIE_EMPLOI_statut_id = T_STATUT_EMPLOI.STATUT_EMPLOI_id) ON T_SALARIE.SALARIE_NNI = T_SALARIE_EMPLOI.SALARIE_EMPLOI_salarie_nni) ON T_EMPLOI.EMPLOI_identifiant = T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant
WHERE (((T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*valid*" Or (T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*décidé*") AND ((T_EMPLOI.EMPLOI_entreprise_id)=1));
This gave me the result I want but with the issue I mentioned previously (Same result for each line)
So
I thought I needed a new Criteria.
I added one, where It's going to select the candidate when the two "emploi ID" of my actual table (Shown before) and the one helping me select the candidates are equal.
With the following query:
SELECT T_SALARIE.SALARIE_nom & " " & T_SALARIE.SALARIE_prenom AS Candidat, T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant
FROM T_EMPLOI INNER JOIN (T_SALARIE INNER JOIN (T_SALARIE_EMPLOI LEFT JOIN T_STATUT_EMPLOI ON T_SALARIE_EMPLOI.SALARIE_EMPLOI_statut_id = T_STATUT_EMPLOI.STATUT_EMPLOI_id) ON T_SALARIE.SALARIE_NNI = T_SALARIE_EMPLOI.SALARIE_EMPLOI_salarie_nni) ON T_EMPLOI.EMPLOI_identifiant = T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant
WHERE (((T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*valid*" Or (T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*décidé*") AND ((T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant)=[R_Select_COMOB]![ACTION_identifiant_emploi]));
But I keep on getting the following pop up that asks me to enter a Job ID
So how can I make the query for each line compare and select the right values?
I hope I was clear in explaining. If not please let me know so that I can add more details.
Thank you !
Thanks to your help and specially #June7 for his propositions, I found a solution regarding my problem :
I added a criteria to select values based on JobID that I wasn't selecting in the first hand.
And then based on the column (jobID) select the values needed
here is my final query :
SELECT
[SALARIE_nom] & " " & [SALARIE_prenom] & " (" & [SALARIE_NNI] & ")" AS Salarié, T_SALARIE_EMPLOI.SALARIE_EMPLOI_salarie_nni, T_SALARIE_EMPLOI.SALARIE_EMPLOI_id, T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant
FROM
(T_STATUT_EMPLOI INNER JOIN T_SALARIE_EMPLOI ON T_STATUT_EMPLOI.STATUT_EMPLOI_id = T_SALARIE_EMPLOI.SALARIE_EMPLOI_statut_id) LEFT JOIN R_Select_Salarie ON T_SALARIE_EMPLOI.SALARIE_EMPLOI_salarie_nni = R_Select_Salarie.SALARIE_NNI
WHERE
(((T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant)=[Formulaires]![F_COMOB]![ACTION_identifiant_emploi]) AND ((T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*validé*") AND ((T_SALARIE_EMPLOI.SALARIE_EMPLOI_Entreprise) Like "*RTE*"));
And Then to update my values for each line. I added a VBA code that requery on input.
Private Sub ACTION_Candidats_P_Enter()
ACTION_Candidats_P.Requery
End Sub
With that my problem is solved.

Find changes made in PGSQL table using SQL in Excel VBA

Is it possible to create an SQL query to compare a field within a single table to see if a change has been made and if possible list the before and after?
I have the following SQL query written in Excel 2010 VBA, which connects to an Oracle PostGreSQL database
Dim au As String
au = "SELECT id, priority, flag, code " _
& "FROM hist WHERE ( aud_dt >= '18/05/2020' AND aud_dt <='18/05/2020' ) " _
Set rs = conn.Execute(au)
With ActiveSheet.QueryTables.Add(Connection:=rs, Destination:=Range("A1"))
.Refresh
End With
Where fields include:
"priority" is the field that I'd like to check for changes which will
be a single number between 0-9
"code" is the record that has been
assigned the priority and is a mixture of numbers and letters up to 7
characters
"flag" shows a 1 as the active record, and 2 as an edited
record
"id" refers to the user account
I'd ideally like to end up with something like: id | priority | flag | priority_old | flag_old | code
Which should show the before and after changes to the priority. If the record shows priority=3 and flag=2 and code=Ab12, there must also be record with a 1 flag, as that is now the active record. If it has the same priority number for the code I'm not interested in it as that just means something else was changed instead as I have not listed all the column fields.
If the active record now shows priority=4, flag=1 and code=Ab12, that would be exactly the record I need to see.
Consider a self-join query (possibly you need to adjust date filter in WHERE depending on when items change):
SELECT h1.id, h1.priority, h1.flag,
h2.priority AS priority_old, h2.flag AS flag_old, h1.code
FROM hist h1
LEFT JOIN hist h2
ON h1.code = h2.code
AND h1.priority <> h2.priority
AND h1.flag = 1
AND h2.flag <> 1
WHERE (aud_dt >= '2020-05-18' AND aud_dt <='2020-05-18')

SQL MAX not returning unique records

I'm using the SQL MAX on a field in my db called "Date_Created", essentially i use it to bring back the most recent record for a particular user. It works fine for all user records that have higher count than 1 but when a user only has one record SQL MAX does not return a record" Can anyone advise what i'm doing wrong ?
code:
"SELECT CP_Score.Credit_Score "
. "FROM CP_Score "
. "INNER JOIN phpro_users ON CP_Score.ID_No=phpro_users.ID_No "
. "WHERE phpro_users.User_ID = $userloggedin "
. " AND CP_Score.Date_Created = (SELECT MAX(CP_Score.Date_Created) "
. " FROM CP_Score)";
SELECT CP_Score.Credit_Score
FROM CP_Score
INNER JOIN phpro_users ON CP_Score.ID_No=phpro_users.ID_No
WHERE phpro_users.User_ID = $userloggedin
AND CP_Score.Date_Created = (SELECT MAX(cps2.Date_Created)
FROM CP_Score cps2
WHERE cps2.ID_No=phpro_users.ID_No)
If you are looking for one record, you can limit the result set to a single record. In ANSI/ISO standard syntax:
SELECT s.Credit_Score
FROM CP_Score s INNER JOIN
phpro_users u
ON s.ID_No = u.ID_No
WHERE u.User_ID = $userloggedin
ORDER BY Date_Created DESC
FETCH FIRST 1 ROW ONLY;
Some databases express the FETCH FIRST clause differently -- SELECT TOP (1) or LIMIT are common.

Pivot table with multiple Values by Date

I was wondering if the solution to the ticket at the following link was ever found
Pivot on multiple fields and export from Access.
I am trying to get results from an access table in the following format where: V1 (value 1) is a number and V2 (value 2) is timestamp (hh:mm:ss).
Basically the report pulls for current month so eventually there would be a day at the top for every day of the month and a corresponding Number and time value for each day.
April 1 April 2 April 3
Manager V1 V2 V1 V2 V1 V2
John Doe 4 5:43:12 1 0:56:32 2 3:15:12
It is an elapsed time. I have been looking at Allen Browne's post and I am most of the way there I think. I created two tables: 1) for V1 which is a number of dials; and 2) for V2 which is total talk time. I then used Allen Browne's method but I cannot get the format of the time to be in hh:mm:ss. It is that way in the base table but whatever I try it always seems to just show 1 digit.
TRANSFORM Sum(IIf([FldName]="DIALS",Val([DIALS].[DIALS]),
Val(Format([T‌​ALKTIME].[TT],"hh:nn‌​:ss")))) AS TheValue
SELECT TALKTIME.Manager, DIALS.TW_Program_Code,
[DIALS]![VSE_FirstName] & " " & [DIALS]![VSE_Surname_Name] AS REP
FROM tblXtabColumns, TALKTIME
INNER JOIN DIALS
ON (TALKTIME.TW_Program_Code = DIALS.TW_Program_Code)
AND (TALKTIME.VSE_FirstName = DIALS.VSE_FirstName)
AND (TALKTIME.VSE_Surname_Name = DIALS.VSE_Surname_Name)
AND (TALKTIME.Period = DIALS.Period) AND (TALKTIME.[Manager] = DIALS.[Manager])
WHERE (((Month([TALKTIME].[Period])) = Month(Now())-1))
GROUP BY TALKTIME.Manager, DIALS.TW_Program_Code,
[DIALS]![VSE_FirstName] & " " & [DIALS]![VSE_Surname_Name]
PIVOT [DIALS].[Period] & " " & [FldName];
Is that time of day or elapsed time?
Review http://allenbrowne.com/ser-67.html#MultipleValues. Cannot have multi-line headers. The date would have to be concatenated with the field that provides the V1 and V2 header. Is there even a field that can be used to provide the "V1" and "V2" headers? If not, calculate it. One way:
SELECT *, DCount("*","Table1","Manager='" & [Manager] & "' AND ID <" & [ID])+1 AS Seq FROM Table1 WHERE Month([datefield]) = 4 AND Year([datefield])=2017;
Now use that query in CROSSTAB. Either build 2 CROSSTAB queries and join them or use Allen Browne approach. Example CROSSTAB to pivot only the "V1" data.
TRANSFORM First(Query1.Reading) AS DailyReading
SELECT Query1.Manager
FROM Query1
GROUP BY Query1.Manager
PIVOT Format([ReadDate],"mmm d") & " : " & [Seq] & "Reading";

LINQ to SQL Query using all memory before timing out

Hi I have a stored procedure within SQL that is a simple select with multiple inner joins, I converted this stored procedure to LINQ and when executing the memory usage climbs until I get out of memory exceptions. I am definitely new to LINQ so I am hoping it is just the way I am doing things. Below is the LINQ code
EDIT: StockRoomID, CustomerID (integers)
StartDate, EndDate (datetime)
variable passed into the function
Dim query = (From prds In context.prds
Join prd_colour In context.prd_colours On prd_colour.prd_id Equals prds.prd_id
Join prd_colour_srsp In context.prd_colour_srsps On prd_colour.prd_colour_id Equals prd_colour_srsp.prd_colour_id
Join prd_br In context.prd_brs On prd_br.prd_colour_id Equals prd_colour.prd_colour_id
Join prd_stk_rm In context.prd_stk_rms On prd_stk_rm.prd_br_id Equals prd_br.prd_br_id
Join ct_colour In context.ct_colours On ct_colour.ct_colour_id Equals prd_colour.ct_colour_id
Join ct_pmg In context.ct_pmgs On ct_pmg.ct_pmg_id Equals prds.ct_pmg_id
Join ct_pmg_detail In context.ct_pmg_details On ct_pmg_detail.ct_pmg_id Equals ct_pmg.ct_pmg_id
Join ct_stk_rm In context.ct_stk_rms On ct_stk_rm.ct_br_id Equals ct_pmg_detail.ct_br_id And ct_stk_rm.ct_stk_rm_id Equals prd_stk_rm.ct_stk_rm_id
Join ct_cust_type In context.ct_cust_types On ct_cust_type.ct_cust_type_id Equals ct_pmg_detail.ct_cust_type_id
Join ct_cust_br In context.ct_cust_brs On ct_cust_br.ct_cust_type_id Equals ct_cust_type.ct_cust_type_id
Where prds.prd_invisible_yn = 0 And prds.sell_yn = 1 And prds.ct_apg_id = 3 And ct_stk_rm.ct_stk_rm_id = StockRoomID And _
ct_cust_br.ct_cust_id = CustomerID And prd_colour_srsp.srsp_date_beg <= StartDate And prd_colour_srsp.srsp_date_end >= EndDate Or _
prd_colour_srsp.srsp_date_end Is Nothing And ct_pmg_detail.pmg_date_beg <= StartDate And ct_pmg_detail.pmg_date_end >= EndDate Or _
ct_pmg_detail.pmg_date_end Is Nothing
Select New prd_colour_mix With {.pid = prd_colour.prd_colour_id, .prdcode = prds.prd_sbcode & "-" & ct_colour.ct_colour_code, .prddesc = prds.prd_desc & "-" & ct_colour.ct_colour_desc, .prdLegacyCode = prd_colour.prd_legacy_code}).ToList
This query should return roughly 7k records, the stored procedure takes about 500ms to execute
Thanks
Most likely it is doing client side something that you want it to do serverside.
Download LINQPad (free), fire it up and add connection to your datasource, select the added datasource as the active one for your query, set Language to "VB Statements" and then run it. Select the "SQL" tab in the result output window and inspect the SQL. Then you'll see what parts of the expression that is handled server side.