SQL consecutive rows with a fixed time span between each row - sql

Good people at StackOverflow,
please be so kind to provide some help...
So what we have here is let's say a table of sort... containing phone calls from customers to some Contact Center (HelpDesk or whatever).
-----------------------------------------------------------------------
| DateD | DateM | Date_Time |EMPL_ID| PHONE_NO |FIRST_REP |
|----------------------------------------------------------------------
|2016-12-12| 2016-12-01| 2016-12-12 15:55| 16652 | 123456789| First |
|2016-12-22| 2016-12-01| 2016-12-22 10:42| 18178 | 123456789| First |
|2016-12-22| 2016-12-01| 2016-12-22 10:54|112981 | 123456789| Repeat |
|2016-12-22| 2016-12-01| 2016-12-22 10:57| 18179 | 123456789| Repeat |
|2016-12-23| 2016-12-01| 2016-12-23 12:27| 16653 | 123456789| Repeat |
|2017-01-05| 2017-01-01| 2017-01-05 15:20| 17896 | 123456789| First |
|2017-01-11| 2017-01-01| 2017-01-11 15:48| 17909 | 123456789| Repeat |
|2017-01-18| 2017-01-01| 2017-01-18 10:07| 18175 | 123456789| Repeat |
|2016-12-03| 2016-12-01| 2016-12-03 20:32| 17745 | 111222333| First |
|2016-12-21| 2016-12-01| 2016-12-21 18:47| 10982 | 111222333| First |
|2016-12-22| 2016-12-01| 2016-12-22 15:53| 17820 | 111222333| Repeat |
|2016-12-28| 2016-12-01| 2016-12-28 13:07| 15976 | 111222333| Repeat |
|2016-12-29| 2016-12-01| 2016-12-29 21:35| 17896 | 111222333| Repeat |
|2016-12-29| 2016-12-01| 2016-12-29 21:46| 15498 | 111222333| Repeat |
|2017-01-02| 2017-01-01| 2017-01-02 16:24| 13117 | 111222333| Repeat |
-----------------------------------------------------------------------
What I would like to do is figure out, how many calls are repeated, meaning that the customer called again.
Now the tricky part is that the repeated calls is defined as a call that originated from the 'first call' and is being repeated consecutively in the time span of 7 days from each interaction after the first call, so for instance:
----------------------------------------------------------------------------
| DateD | DateM | Date_Time |EMPL_ID| PHONE_NO |FIRST_REP |
|---------------------------------------------------------------------------
|2016-12-01 | 2016-12-12 | 2016-12-12 15:55 | 16652 | 123456789 | First |
|2016-12-01 | 2016-12-22 | 2016-12-22 10:42 | 18178 | 123456789 | First |
|2016-12-01 | 2016-12-22 | 2016-12-22 10:54 | 112981| 123456789 | Repeat |
|2016-12-01 | 2016-12-22 | 2016-12-22 10:57 | 18179 | 123456789 | Repeat |
|2016-12-01 | 2016-12-23 | 2016-12-23 12:27 | 16653 | 123456789 | Repeat |
|2017-01-01 | 2017-01-05 | 2017-01-05 15:20 | 17896 | 123456789 | First |
|2017-01-01 | 2017-01-11 | 2017-01-11 15:48 | 17909 | 123456789 | Repeat |
|2017-01-01 | 2017-01-18 | 2017-01-18 10:07 | 18175 | 123456789 | Repeat |
----------------------------------------------------------------------------
we've got :
1st row that is a First Call with no repeated calls,
2sd row that is a First Call with 3 repeated calls as every interaction is in the time span of 7 days from each previous one beginning from the First Calls
3rd row that is a First Call with 2 repeated calls just like above.
And now what we want to say is that employee (1st row) with the ID 16652 generated 0 repeated calls but the employee withe the ID 18178 generated on the other hand 3 repeated calls.
Finally it would be great to have some method that would allow to create an output like this:
| DateM | DateD | Date_Time |EMP_ID | PHONE_NO |FIRST_REP | DateM_REP | DateD_REP | Date_Time_REP | EMP_ID_REP | PHONE_NO_REP | FIRST_REP_REP
|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|2016-12-01 | 2016-12-12 | 2016-12-12 15:55 | 16652 | 123456789 | First | null | null | null | null | null | null
|2016-12-01 | 2016-12-22 | 2016-12-22 10:42 | 18178 | 123456789 | First | 2016-12-01 | 2016-12-22 | 2016-12-22 10:54 | 112981 | 123456789 | Repeat
|2016-12-01 | 2016-12-22 | 2016-12-22 10:42 | 18178 | 123456789 | First | 2016-12-01 | 2016-12-22 | 2016-12-22 10:57 | 18179 | 123456789 | Repeat
|2016-12-01 | 2016-12-22 | 2016-12-22 10:42 | 18178 | 123456789 | First | 2016-12-01 | 2016-12-23 | 2016-12-23 12:27 | 16653 | 123456789 | Repeat
|2017-01-01 | 2017-01-05 | 2017-01-05 15:20 | 17896 | 123456789 | First | 2017-01-01 | 2017-01-11 | 2017-01-11 15:48 | 17909 | 123456789 | Repeat
|2017-01-01 | 2017-01-05 | 2017-01-05 15:20 | 17896 | 123456789 | First | 2017-01-01 | 2017-01-18 | 2017-01-18 10:07 | 18175 | 123456789 | Repeat
Please help, I'm not that good at writing CTE and as I imagine that's a kind problem that has the potential of being solved with CTE.
much oblidged
LuKI.
edit:
CREATE TABLE t_calls
(
[DateM] date,
[DateD] date,
[Date_Time] datetime2(7),
[EMPL_ID] int,
[INTERACTION_ID] numeric(25,0),
[PHONE_NO] numeric(9,0),
[FIRST_REP] varchar(10)
)
Insert Into t_calls
([DateM],[DateD],[Date_Time],[EMPL_ID],[INTERACTION_ID],[PHONE_NO],[FIRST_REP])
Values
('2016-12-01 00:00:00','2016-12-12 00:00:00','2016-12-12 15:55:36',16652,340680165,123456789,'First')
,('2016-12-01 00:00:00','2016-12-22 00:00:00','2016-12-22 10:42:45',18178,343736497,123456789,'First')
,('2016-12-01 00:00:00','2016-12-22 00:00:00','2016-12-22 10:54:46',112981,343750151,123456789,'Repeat')
,('2016-12-01 00:00:00','2016-12-22 00:00:00','2016-12-22 10:57:29',18179,343750151,123456789,'Repeat')
,('2016-12-01 00:00:00','2016-12-23 00:00:00','2016-12-23 12:27:56',16653,344071359,123456789,'Repeat')
,('2017-01-01 00:00:00','2017-01-05 00:00:00','2017-01-05 15:20:47',17896,347063121,123456789,'First')
,('2017-01-01 00:00:00','2017-01-11 00:00:00','2017-01-11 15:48:20',17909,348429965,123456789,'Repeat')
,('2017-01-01 00:00:00','2017-01-18 00:00:00','2017-01-18 10:07:45',18175,350243945,123456789,'Repeat')
,('2016-12-01 00:00:00','2016-12-03 00:00:00','2016-12-03 20:32:37',17745,338392721,111222333,'First')
,('2016-12-01 00:00:00','2016-12-21 00:00:00','2016-12-21 18:47:12',10982,343633967,111222333,'First')
,('2016-12-01 00:00:00','2016-12-22 00:00:00','2016-12-22 15:53:59',17820,343885389,111222333,'Repeat')
,('2016-12-01 00:00:00','2016-12-28 00:00:00','2016-12-28 13:07:19',15976,344944219,111222333,'Repeat')
,('2016-12-01 00:00:00','2016-12-29 00:00:00','2016-12-29 21:35:44',17896,345396945,111222333,'Repeat')
,('2016-12-01 00:00:00','2016-12-29 00:00:00','2016-12-29 21:46:43',15498,345398005,111222333,'Repeat')
,('2017-01-01 00:00:00','2017-01-02 00:00:00','2017-01-02 16:24:12',13117,346045147,111222333,'Repeat')

If I understand your question what you want to know for every call, how many calls were repeated within 7 days.
SELECT
a.date_time
,a.emp_id
,a.phone_no
,count(b.phone_no) as repeat_calls --count any non-null field
,min(b.date_time) as first_repeat_call_at
FROM t_calls a
LEFT JOIN t_calls b
ON a.phone_no = b.phone_no --same phone
AND datediff(d, a.date_time, b.date_time) between 0 AND 6 --a repeat comes in today + 6 days
AND a.date_time < b.date_time --prevents self join
GROUP BY
a.date_time
,a.emp_id
,a.phone_no
For any call with 0 repeats, there'll be nothing to join, thus nothing to count so repeat_calls = 0 and first_repeat_call_at is NULL.

Related

Date compare between two datetime columns and a constant

Lets say I have this following table:
+------+-------------------------+-------------------------+
| ID | CreatedDate | LastChangedDate |
+------+-------------------------+-------------------------+
| 3965 | 2019-01-23 03:54:44.903 | 2021-03-12 06:24:45.390 |
+------+-------------------------+-------------------------+
| 3966 | 2019-01-23 03:55:37.160 | 2021-01-09 04:50:20.697 |
+------+-------------------------+-------------------------+
| 3967 | 2019-01-23 03:56:21.197 | 2020-05-11 06:10:14.203 |
+------+-------------------------+-------------------------+
| 3968 | 2019-01-23 03:57:07.943 | 2020-05-11 11:28:26.580 |
+------+-------------------------+-------------------------+
| 3969 | 2019-01-23 03:58:01.020 | NULL |
+------+-------------------------+-------------------------+
| 3970 | 2019-01-23 03:58:42.293 | 2021-05-11 09:57:54.553 |
+------+-------------------------+-------------------------+
| 4143 | 2019-03-19 04:23:08.003 | 2020-12-14 10:08:38.303 |
+------+-------------------------+-------------------------+
| 4144 | 2019-03-19 04:51:14.533 | 2020-12-14 10:05:11.867 |
+------+-------------------------+-------------------------+
| 4145 | 2019-03-19 05:16:28.980 | 2019-07-11 07:23:15.803 |
+------+-------------------------+-------------------------+
| 4146 | 2019-03-19 05:18:49.550 | 2020-01-02 09:12:13.597 |
+------+-------------------------+-------------------------+
| 4808 | 2019-09-17 05:44:54.587 | 2021-01-09 10:35:20.860 |
+------+-------------------------+-------------------------+
| 5243 | 2020-01-02 09:07:10.573 | 2021-02-01 16:06:51.770 |
+------+-------------------------+-------------------------+
| 5666 | 2020-08-12 07:16:20.617 | 2021-01-09 04:52:25.427 |
+------+-------------------------+-------------------------+
| 5877 | 2020-09-05 05:35:56.160 | 2021-01-09 04:51:43.707 |
+------+-------------------------+-------------------------+
Now lets say I want to see whether CreatedDate or LastChangedDate column is greater than '2021-01-09'.
To do the comparison, I need to check if LastChangedDate is NULL then compare the given date with CreatedDate field, else compare with LastChangedDate field.
What I have tried so far:
DECLARE #test_date as varchar(20) = '2021-01-09'
SELECT ID, CreatedDate, LastChangedDate
FROM #temptable
WHERE CAST(ISNULL(LastChangedDate,CreatedDate) as date) > CAST(#test_date as date)
But it is not giving proper output.
What I want is:
+------+-------------------------+-------------------------+
| ID | CreatedDate | LastChangedDate |
+------+-------------------------+-------------------------+
| 3967 | 2019-01-23 03:56:21.197 | 2020-05-11 06:10:14.203 |
+------+-------------------------+-------------------------+
| 3968 | 2019-01-23 03:57:07.943 | 2020-05-11 11:28:26.580 |
+------+-------------------------+-------------------------+
| 3969 | 2019-01-23 03:58:01.020 | NULL |
+------+-------------------------+-------------------------+
| 4143 | 2019-03-19 04:23:08.003 | 2020-12-14 10:08:38.303 |
+------+-------------------------+-------------------------+
| 4144 | 2019-03-19 04:51:14.533 | 2020-12-14 10:05:11.867 |
+------+-------------------------+-------------------------+
| 4145 | 2019-03-19 05:16:28.980 | 2019-07-11 07:23:15.803 |
+------+-------------------------+-------------------------+
| 4146 | 2019-03-19 05:18:49.550 | 2020-01-02 09:12:13.597 |
+------+-------------------------+-------------------------+
Also here is a sqlplayground with sample data
In fact you shouldn't need any CASTs:
first define your variable as a date instead of a string - always use the correct datatype for the data you are storing.
second use logical operations (AND/OR) instead of COALESCE to make your query sargable i.e. able to use indexes. Anytime you use a function on a column in your WHERE clause you run the risk of preventing the use of indexes and slowing your query down.
DECLARE #test_date as date = '2021-01-09';
SELECT ID, CreatedDate, LastChangedDate
FROM #temptable
WHERE (LastChangedDate IS NOT NULL AND LastChangedDate > #test_date)
OR (LastChangedDate IS NULL AND CreatedDate > #test_date);
I think you are comparing only date. So try with compare date and time both.
SELECT ID,CreatedDate, LastChangedDate
FROM #temptable WHERE
CAST(ISNULL(LastChangedDate,CreatedDate) as datetime) > CAST(#test_date as datetime)

Return in only one table info from two tables into rows when info is in columns

In PostgreSQL I have 2 tables with fields:
Working_date: id (autonumeric), employee_code (varchar (6)), working_date (date), working_hour (time),
Attendance: id (autonumeric), employee_code (varchar (6)), attendance_date (date), attendance_hour (time),
Data example:
Working_date
ID | employee_code | working_date | working_hour
1 | 12345 | 2015-07-09 | 08:00
2 | 12345 | 2015-07-09 | 13:00
3 | 12345 | 2015-07-09 | 14:00
4 | 12345 | 2015-07-09 | 17:00
5 | 12345 | 2015-07-10 | 08:00
6 | 12345 | 2015-07-10 | 13:00
7 | 12345 | 2015-07-10 | 14:00
8 | 12345 | 2015-07-10 | 17:00
9 | 12345 | 2015-07-11 | 08:00
10 | 12345 | 2015-07-11 | 13:00
11 | 12345 | 2015-07-11 | 14:00
12 | 12345 | 2015-07-11 | 17:00
13 | 12345 | 2015-07-12 | 08:00
14 | 12345 | 2015-07-12 | 13:00
15 | 12345 | 2015-07-12 | 14:00
16 | 12345 | 2015-07-12 | 17:00
17 | 12345 | 2015-07-13 | 08:00
18 | 12345 | 2015-07-13 | 13:00
19 | 12345 | 2015-07-13 | 14:00
20 | 12345 | 2015-07-13 | 17:00
Attendance
ID | employee_code | attendance_date | attendance_hour
1 | 12345 | 2015-07-09 | 07:56:53
2 | 12345 | 2015-07-09 | 10:33:31
3 | 12345 | 2015-07-09 | 13:00:42
4 | 12345 | 2015-07-09 | 13:00:47
5 | 12345 | 2015-07-09 | 13:30:21
6 | 12345 | 2015-07-09 | 17:00:01
7 | 12345 | 2015-07-10 | 07:48:35
8 | 12345 | 2015-07-10 | 12:15:20
9 | 12345 | 2015-07-10 | 13:58:42
10 | 12345 | 2015-07-10 | 17:02:00
11 | 12345 | 2015-07-11 | 08:06:46
12 | 12345 | 2015-07-11 | 12:00:01
13 | 12345 | 2015-07-11 | 13:52:01
14 | 12345 | 2015-07-11 | 17:05:08
15 | 12345 | 2015-07-12 | 07:55:02
16 | 12345 | 2015-07-12 | 12:03:22
17 | 12345 | 2015-07-12 | 13:37:40
18 | 12345 | 2015-07-12 | 17:05:01
19 | 12345 | 2015-07-13 | 07:54:25
20 | 12345 | 2015-07-13 | 10:44:15
21 | 12345 | 2015-07-13 | 13:59:21
22 | 12345 | 2015-07-13 | 17:01:17
In table "Attendance" there are some repetitive rows because employee entered attendance more than once. For example on 2015-07-09 there are 2 attendance times (13:00:42, 13:00:47) when it is time to go out for lunch. In this case, I should get only one of the two records.
The other case on 2015-07-09 there is 10:33:31. It is recorded when the employee asks permission to leave work and then returns in this case at 13:00:42 / 13:00:47.
Is there a way for getting working_date, working_hour with its respective attendance_hour in one table with only pure SQL queries (maybe some type of subqueries)?
Example:
ID | employee_code | working_date | working_hour1 | attendance_time_1 | working_hour2 | attendance_time_2 | working_hour3 | attendance_time_3 | working_hour4 | attendance_time_4
1 | 12345 | 2015-07-09 | 08:00 | 07:56:53 | 13:00:00 | 13:00:42 or 13:00:47 | 14:00 | 13:30:21 | 17:00 | 17:00:01
2 | 12345 | 2015-07-10 | 08:00 | 07:48:35 | 13:00:00 | 12:15:20 | 14:00 | 13:58:42 | 17:00 | 17:02:00
3 | 12345 | 2015-07-11 | 08:00 | 08:06:46 | 13:00:00 | 12:00:01 | 14:00 | 13:52:01 | 17:00 | 17:05:08
4 | 12345 | 2015-07-12 | 08:00 | 07:55:02 | 13:00:00 | 12:03:22 | 14:00 | 13:37:40 | 17:00 | 17:05:01
5 | 12345 | 2015-07-13 | 08:00 | 07:54:25 | 13:00:00 | 10:44:15 | 14:00 | 13:59:21 | 17:00 | 17:01:17
In case it is not possible to get it with pure SQL querie, how can it achieved with maybe PL/PGSQL?
Currently I make it with PHP like this:
I query employee_code and working_date fields from working_date table. This query is run between 2 dates: from_date, to_date.
Inside a "for statement" I consult with every row of working_date all the working_hour rows: working_hour1, working_hour2, working_hour3, working_hour4. For every row is run an SQL query. For this query I send it employee_code and working_date parameters.
Inside a nested "for statement", with every working_hour, I run a query to "attendance_date" table with parameters: employee_code, working_date and working_hour. It returns the attendance_hour for every working_hour.
This way (calling SELECTS from PHP with nested "for statement") is too slow for getting and showing the info. I see the process when executing it and process takes 100% of CPU.
You can join those tables on their dates and aggregate the attendance times into an array by grouping by date and employee_code somewhat like this:
SELECT
w.employee_code,
w.working_date,
array_agg(distinct(w.working_hour)) working_hours,
array_agg(distinct(a.attendance_hour)) attendance_hours
FROM Working_date w
LEFT JOIN attendance a
ON (w.working_date = a.attendance_date)
GROUP BY w.working_date, w.employee_code
ORDER BY w.working_date
You could use the unnest() function of postgres to unnest those arrays, but it will put them into new rows not columns. Putting them into separate columns is difficult because those arrays probably won't be the same length and all rows have to have the same columns.
Heres a fiddle
http://sqlfiddle.com/#!15/2a75c/7/0

How to update column with average weekly value for each day in sql

I have the following table. I insert a column named WeekValue, I want to fill the weekvalue column with the weekly average value of impressionCnt of the same category for each row.
Like:
+-------------------------+----------+---------------+--------------+
| Date | category | impressioncnt | weekAverage |
+-------------------------+----------+---------------+--------------+
| 2014-02-06 00:00:00.000 | a | 123 | 100 |
| 2014-02-06 00:00:00.000 | b | 121 | 200 |
| 2014-02-06 00:00:00.000 | c | 99 | 300 |
| 2014-02-07 00:00:00.000 | a | 33 | 100 |
| 2014-02-07 00:00:00.000 | b | 456 | 200 |
| 2014-02-07 00:00:00.000 | c | 54 | 300 |
| 2014-02-08 00:00:00.000 | a | 765 | 100 |
| 2014-02-08 00:00:00.000 | b | 78 | 200 |
| 2014-02-08 00:00:00.000 | c | 12 | 300 |
| ..... | | | |
| 2014-03-01 00:00:00.000 | a | 123 | 111 |
| 2014-03-01 00:00:00.000 | b | 121 | 222 |
| 2014-03-01 00:00:00.000 | c | 99 | 333 |
| 2014-03-02 00:00:00.000 | a | 33 | 111 |
| 2014-03-02 00:00:00.000 | b | 456 | 222 |
| 2014-03-02 00:00:00.000 | c | 54 | 333 |
| 2014-03-03 00:00:00.000 | a | 765 | 111 |
| 2014-03-03 00:00:00.000 | b | 78 | 222 |
| 2014-03-03 00:00:00.000 | c | 12 | 333 |
+-------------------------+----------+---------------+--------------+
I tried
update [dbo].[RetailTS]
set Week = datepart(day, dateDiff(day, 0, [Date])/7 *7)/7 +1
To get the week numbers then try to group by the week week number and date and category, but this seems isn't correct. How do I write the SQL query? Thanks!
Given that you may be adding more data in the future, thus requiring another update, you might want to just select out the weekly averages:
SELECT
Date,
category,
impressioncnt,
AVG(impressioncnt) OVER
(PARTITION BY category, DATEDIFF(d, 0, Date) / 7) AS weekAverage
FROM RetailTS
ORDER BY
Date, category;

SQL - Get records matching with other record in the same table

I am using Amazon RedShift PostgreSQL database.
There is a table in my database which stores appointment records like this:
| id | patientname | providername | eventid | eventstart | eventend | isactive | segmentcode | Description |
|----|---------------|--------------|---------|---------------------|---------------------|----------|-------------|------------------------|
| 1 | Susie Jones | John Melton | 340000 | 2017-08-08 10:00:00 | 2017-08-08 10:00:00 | true | 845685 | Reminder Call |
| 2 | Susie Jones | John Melton | 340000 | 2017-08-08 10:00:00 | 2017-08-08 10:30:00 | true | 365478 | Steam Therapy Session |
| 3 | Roschel Ross | Kate Winny | 350000 | 2017-08-09 11:00:00 | 2017-08-09 11:00:00 | true | 845685 | Reminder Call |
| 4 | Roschel Ross | Kate Winny | 350000 | 2017-08-09 11:00:00 | 2017-08-09 13:30:00 | true | 367545 | Physio Therapy Session |
| 5 | Lilly Hodge | Jessica | 360000 | 2017-08-09 11:00:00 | 2017-08-09 11:00:00 | true | 754544 | Doctor appointment |
| 6 | Jack Richards | Mike Chong | 37000 | 2017-08-10 17:00:00 | 2017-08-10 17:30:00 | true | 889754 | Sample Appointment |
| 7 | Sammy Jones | Winsten | 38000 | 2017-08-10 17:00:00 | 2017-08-10 18:30:00 | true | 845685 | Physio Therapy Session |
| 8 | Sammy Jones | Winsten | 38000 | 2017-08-10 17:00:00 | 2017-08-10 17:00:00 | true | 454542 | Reminder Call |
Here, you can see there are some records with segmentcode 845685, and all these records are having a duplicate record with different segment code but the same eventid.
What I want is, using SQL query get the records with segment code 845685 along with its duplicate record. So the resultant table will be:
| id | patientname | providername | eventid | eventstart | eventend | isactive | segmentcode | Description |
|----|---------------|--------------|---------|---------------------|---------------------|----------|-------------|------------------------|
| 1 | Susie Jones | John Melton | 340000 | 2017-08-08 10:00:00 | 2017-08-08 10:00:00 | true | 845685 | Reminder Call |
| 2 | Susie Jones | John Melton | 340000 | 2017-08-08 10:00:00 | 2017-08-08 10:30:00 | true | 365478 | Steam Therapy Session |
| 3 | Roschel Ross | Kate Winny | 350000 | 2017-08-09 11:00:00 | 2017-08-09 11:00:00 | true | 845685 | Reminder Call |
| 4 | Roschel Ross | Kate Winny | 350000 | 2017-08-09 11:00:00 | 2017-08-09 13:30:00 | true | 367545 | Physio Therapy Session |
| 7 | Sammy Jones | Winsten | 38000 | 2017-08-10 17:00:00 | 2017-08-10 18:30:00 | true | 845685 | Physio Therapy Session |
| 8 | Sammy Jones | Winsten | 38000 | 2017-08-10 17:00:00 | 2017-08-10 17:00:00 | true | 454542 | Reminder Call |
How can I do it?
It seems that simple subquery (as one way), will be sufficient:
select * from t
where eventid in (select eventid from t where segmentcode = 845685)
-- order by eventid, ...
another way is using exists :
select * from t t1
where
exists (select 1 from t t2 where eventid = t1.eventid and segmentcode = 845685 )
thousands of records are not so big amount for modern databases, any way, if performance is not desirable, adding indexes (if not have already) on eventid and segmentcode should speed up select queries.

How to conditionally count rows from another table WITHOUT USING A CORRELATED SUBQUERY?

I have a dataset for which I have to conditionally count rows from table B that are between two dates in table A. I have to do this without the use of a correlated subquery in the SELECT clause, as this is not supported in Netezza - docs: https://www.ibm.com/support/knowledgecenter/en/SSULQD_7.0.3/com.ibm.nz.dbu.doc/c_dbuser_correlated_subqueries_ntz_sql.html.
Background on tables: Users can log in to a site (logins). When they log in, they can take actions, which are in (actions_taken). The desired output is a count of rows that are between the actions_taken action_date and lag_action_date.
Data and attempt found here: http://rextester.com/NLDH13254
Table: actions_taken (with added calculations - see RexTester.)
| user_id | action_type | action_date | lag_action_date | elapsed_days |
|---------|---------------|-------------|-----------------|--------------|
| 12345 | action_type_1 | 6/27/2017 | 3/3/2017 | 116 |
| 12345 | action_type_1 | 3/3/2017 | 2/28/2017 | 3 |
| 12345 | action_type_1 | 2/28/2017 | NULL | NULL |
| 12345 | action_type_2 | 3/6/2017 | 3/3/2017 | 3 |
| 12345 | action_type_2 | 3/3/2017 | 3/25/2016 | 343 |
| 12345 | action_type_2 | 3/25/2016 | NULL | NULL |
| 12345 | action_type_4 | 3/6/2017 | 3/3/2017 | 3 |
| 12345 | action_type_4 | 3/3/2017 | NULL | NULL |
| 99887 | action_type_1 | 4/1/2017 | 2/11/2017 | 49 |
| 99887 | action_type_1 | 2/11/2017 | 1/28/2017 | 14 |
| 99887 | action_type_1 | 1/28/2017 | NULL | NULL |
Table: logins
| user_id | login_date |
|---------|------------|
| 12345 | 6/27/2017 |
| 12345 | 6/26/2017 |
| 12345 | 3/7/2017 |
| 12345 | 3/6/2017 |
| 12345 | 3/3/2017 |
| 12345 | 3/2/2017 |
| 12345 | 3/1/2017 |
| 12345 | 2/28/2017 |
| 12345 | 2/27/2017 |
| 12345 | 2/25/2017 |
| 12345 | 3/25/2016 |
| 12345 | 3/23/2016 |
| 12345 | 3/20/2016 |
| 99887 | 6/27/2017 |
| 99887 | 6/26/2017 |
| 99887 | 6/24/2017 |
| 99887 | 4/2/2017 |
| 99887 | 4/1/2017 |
| 99887 | 3/30/2017 |
| 99887 | 3/8/2017 |
| 99887 | 3/6/2017 |
| 99887 | 3/3/2017 |
| 99887 | 3/2/2017 |
| 99887 | 2/28/2017 |
| 99887 | 2/11/2017 |
| 99887 | 1/28/2017 |
| 99887 | 1/26/2017 |
| 99887 | 5/28/2016 |
DESIRED OUTPUT: cnt_logins_between_action_dates field
| user_id | action_type | action_date | lag_action_date | elapsed_days | cnt_logins_between_action_dates |
|---------|---------------|-------------|-----------------|--------------|---------------------------------|
| 12345 | action_type_1 | 6/27/2017 | 3/3/2017 | 116 | 5 |
| 12345 | action_type_1 | 3/3/2017 | 2/28/2017 | 3 | 4 |
| 12345 | action_type_1 | 2/28/2017 | NULL | NULL | 1 |
| 12345 | action_type_2 | 3/6/2017 | 3/3/2017 | 3 | 2 |
| 12345 | action_type_2 | 3/3/2017 | 3/25/2016 | 343 | 7 |
| 12345 | action_type_2 | 3/25/2016 | NULL | NULL | 1 |
| 12345 | action_type_4 | 3/6/2017 | 3/3/2017 | 3 | 2 |
| 12345 | action_type_4 | 3/3/2017 | NULL | NULL | 1 |
| 99887 | action_type_1 | 4/1/2017 | 2/11/2017 | 49 | 8 |
| 99887 | action_type_1 | 2/11/2017 | 1/28/2017 | 14 | 2 |
| 99887 | action_type_1 | 1/28/2017 | NULL | NULL | 1 |
You don't need a correlated sub-query. Get the previous date using lag and join the logins table to count the actions between dates.
with prev_dates as (select at.*
,coalesce(lag(action_date) over(partition by user_id,action_type order by action_date)
,action_date) as lag_action_date
from actions_taken at
)
select at.user_id,at.action_type,at.action_date,at.lag_action_date
,at.action_date-at.lag_action_date as elapsed_days
,count(*) as cnt
from prev_dates at
join login l on l.user_id=at.user_id and l.login_date<=at.action_date and l.login_date>=at.lag_action_date
group by at.user_id,at.action_type,at.action_date,at.lag_action_date
order by 1,2,3