Finding time differences between each row in a specific column in SQL Server - sql

Still learning SQL, but I'm trying to see if there are any Customers that have a time frame within 24 hours of each other. So in this example ID 1 and 4 meet this criteria.
CustID Date
1 2018-04-10 11:21:00.000
1 2018-03-05 18:14:00.000
1 2018-03-05 22:53:00.000
2 2018-04-10 11:21:00.000
2 2018-03-27 14:57:00.000
2 2018-04-04 20:00:00.000
3 2018-04-10 11:21:00.000
3 2018-02-10 11:21:00.000
3 2018-04-24 11:29:00.000
4 2018-04-10 11:21:00.000
4 2018-04-10 11:20:00.000
4 2018-04-24 11:29:00.000
I'm thinking about doing something like
SELECT CustId
From Cars c
CROSS APPLY(
SELECT Date
FROM Cars
Where Date != c.Date)
WHERE Date - c.Date < 24 hours

Use lag():
select distinct custid
from (select c.*,
lag(c.date) over (partition by c.custid order by c.date) as prev_date
from cars c
) c
where date < dateadd(hour, 24, prev_date);

This answer is based on sql-server, but you should be able to translate as needed. I also assumed you had a requirement where the same datetime between two customers can't be the same. If that's a false assumption, remove the where clause. A simple self-join should get you there.
declare #t table (id int, dt datetime)
insert into #t values ('1','2018-04-10 11:21:00.000')
insert into #t values ('1','2018-03-05 18:14:00.000')
insert into #t values ('1','2018-03-05 22:53:00.000')
insert into #t values ('2','2018-04-10 11:21:00.000')
insert into #t values ('2','2018-03-27 14:57:00.000')
insert into #t values ('2','2018-04-04 20:00:00.000')
insert into #t values ('3','2018-04-10 11:21:00.000')
insert into #t values ('3','2018-02-10 11:21:00.000')
insert into #t values ('3','2018-04-24 11:29:00.000')
insert into #t values ('4','2018-04-10 11:21:00.000')
insert into #t values ('4','2018-04-10 11:20:00.000')
insert into #t values ('4','2018-04-24 11:29:00.000')
select
t1.id, t2.id
from #t t1
join #t t2 on t2.dt between dateadd(hh, -24,t1.dt) and t1.dt and t1.id<>t2.id
where t1.dt<>t2.dt

Related

Return number of rows dependent on number

Have a table with this
Id
StartDate
NoOfMonths
1
2021-09-01
2
2
2021-09-01
3
And want a query to return this
Id
Date
1
2021-09-01
1
2021-10-01
2
2021-09-01
2
2021-10-01
2
2021-11-01
How can I make this happen?
Here is an example without an additional table:
DECLARE #t TABLE(
ID int
, StartDate date
, NoOfMonths int
)
INSERT INTO #t VALUES
(1, '2021-09-01', 2)
,(2, '2021-09-01', 3);
WITH cte AS(
SELECT ID, StartDate, NoOfMonths
FROM #t
UNION ALL
SELECT ID, DATEADD(MONTH, 1, StartDate), NoOfMonths-1
FROM cte
WHERE NoOfMonths > 1
)
SELECT ID, StartDate
FROM cte
ORDER BY ID, StartDate
This could be solved by having an additional calendar table, which would be populated and maintained by you. The content of the table could be just dates (first days of months). Then you would join records from that calendar table with your original table using DATEADD() function if it's MS SQL server. So something like:
select DateMonth
from CalendarTable ct
inner join YourTable yt
on ct.DateMonth between yt.StartDate and DATEADD (MONTH, yt.NoOfMonths, yt.StartDate)

Counting rows between dates using row number?

I am trying to find the number of rows that 2 dates fall between. Basically I have an auth dated 1/1/2018 - 4/1/2018 and I need the count of pay periods those dates fall within.
Here is the data I am looking at:
create table #dates
(
pp_start_date date,
pp_end_date date
)
insert into #dates (pp_start_date,pp_end_date)
values ('2017-12-28', '2018-01-10'),
('2018-01-11', '2018-01-24'),
('2018-01-25', '2018-02-07'),
('2018-02-08', '2018-02-21'),
('2018-02-22', '2018-03-07'),
('2018-03-08', '2018-03-21'),
('2018-03-22', '2018-04-04'),
('2018-04-05', '2018-04-18');
When I run this query,
SELECT
ad.pp_start_date, ad.pp_end_date, orderby
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY pp_start_date) AS orderby, *
FROM
#dates) ad
WHERE
'2018-01-01' <= ad.pp_end_date
I somehow want to only get 7 rows. Is this even possible? Thanks in advance for any help!
EDIT - Ok so using a count(*) worked to get the number of rows but now I am trying to get the number of rows for 2 dynamic dates form another temp table but I don't see a way to relate the data.
Using the #dates temp table referenced above gives me the date data. Now using this data:
create table #stuff
([month] date,
[name] varchar(20),
units int,
fips_code int,
auth_datefrom date,
auth_dateto date)
insert into #stuff (month,name,units,fips_code,auth_datefrom,auth_dateto)
values ('2018-01-01','SMITH','50','760', '2018-01-01', '2018-04-01');
insert into #stuff (month,name,units,fips_code,auth_datefrom,auth_dateto)
values ('2018-01-01','JONES','46','193', '2018-01-01', '2018-04-01');
insert into #stuff (month,name,units,fips_code,auth_datefrom,auth_dateto)
values ('2018-01-01','DAVID','84','109', '2018-02-01', '2018-04-01');
I want to somehow create a statement that does a count of rows from the #dates table where the auth dates are referenced in the #stuff table I just can't figure out how to relate them or join them.
pp_start_date <= auth_dateto and pp_end_date >= auth_datefrom
Here is my output for #dates
pp_start_date pp_end_date
2017-12-28 2018-01-10
2018-01-11 2018-01-24
2018-01-25 2018-02-07
2018-02-08 2018-02-21
2018-02-22 2018-03-07
2018-03-08 2018-03-21
2018-03-22 2018-04-04
2018-04-05 2018-04-18
Here is my output for #stuff
month name units fips_code auth_datefrom auth_dateto
2018-01-01 SMITH 50 760 2018-01-01 2018-04-01
2018-01-01 JONES 46 193 2018-01-01 2018-04-01
2018-01-01 DAVID 84 109 2018-02-01 2018-04-01
I am trying to use the auth_datefrom and auth_dateto from #stuff to find out how many rows that is from #dates.
try this one.
SELECT ad.pp_start_date, ad.pp_end_date, orderby
from (select
row_number()over ( order by pp_start_date) as orderby, * from
#dates) ad
where ad.pp_end_date <= '2018-01-01'
or ad.pp_start_date >= '2018-01-01'
Are you looking for this?
select d.*
from #dates d
where d.startdate <= '2018-04-01' and
d.enddate >= '2018-01-01';
This returns all rows that have a date with the time period you specify.
I'm not sure what the row_number() does. If you want the count, then:
select count(*)
from #dates d
where d.startdate <= '2018-04-01' and
d.enddate >= '2018-01-01';

SQL - How to cross-join two table to repeat values

I have a 2 tables that look like this:
MonthEndDate
2016-06-30 00:00:00.000
2016-07-31 00:00:00.000
2016-08-31 00:00:00.000
2016-09-30 00:00:00.000
2016-10-31 00:00:00.000
2016-11-30 00:00:00.000
2016-12-31 00:00:00.000
AND
MonthEndDate CustomerId Flag
2016-06-30 00:00:00.000 123 1
2016-07-31 00:00:00.000 123 1
2016-08-31 00:00:00.000 123 1
2016-09-30 00:00:00.000 123 1
I would like an output that looks like this:
MonthEndDate CustomerId Flag
2016-06-30 00:00:00.000 123 1
2016-07-31 00:00:00.000 123 1
2016-08-31 00:00:00.000 123 1
2016-09-30 00:00:00.000 123 1
2016-10-31 00:00:00.000 123 0
2016-11-30 00:00:00.000 123 0
2016-12-31 00:00:00.000 123 0
Table 1 is a DimDate table that has month end date.
Table
2 is the CustomerInfo table.
Each customer has a Flag set to 1 whenever that customer has a value for the given Month End.
I want to get an output that will have every Month End Date (that's why I'm suing DimDate table) and when a customer does not have a value for the Month End I want the flag to show 0.
I'm using SQL Server 2005
Here is some sample code I used:
DECLARE #table1 TABLE
(
MonthEndDate DATETIME
)
INSERT INTO #table1
VALUES('2016-06-30 00:00:00.000')
INSERT INTO #table1
VALUES('2016-07-31 00:00:00.000')
INSERT INTO #table1
VALUES('2016-08-31 00:00:00.000')
INSERT INTO #table1
VALUES('2016-09-30 00:00:00.000')
INSERT INTO #table1
VALUES('2016-10-31 00:00:00.000')
INSERT INTO #table1
VALUES('2016-11-30 00:00:00.000')
INSERT INTO #table1
VALUES('2016-12-31 00:00:00.000')
DECLARE #table2 TABLE
(
MonthEndDate DATETIME
,CustomerId INT
,Flag INT
)
INSERT INTO #table2
VALUES('2016-06-30 00:00:00.000',123,1)
INSERT INTO #table2
VALUES('2016-07-31 00:00:00.000',123,1)
INSERT INTO #table2
VALUES('2016-08-31 00:00:00.000',123,1)
INSERT INTO #table2
VALUES('2016-09-30 00:00:00.000',123,1)
SELECt * FROM #table1
SELECt * FROM #table2
You need to do a CROSS JOIN on to get all combinations of MonthEndDate and CustomerId. When you have that, do a LEFT JOIN on table2 to get the Flag:
SELECT
t1.MonthEndDate,
c.CustomerId,
Flag = ISNULL(t2.Flag, 0)
FROM #table1 t1
CROSS JOIN (SELECT DISTINCT CustomerId FROM #table2) c
LEFT JOIN #table2 t2
ON t1.MonthEndDate = t2.MonthEndDate
AND c.CustomerId = t2.CustomerId
I think you just want a left join:
select t1.*, coalesce(t2.flag, 0) as flag
from #table1 t1 left join
#table2 t2
on t1.MonthEndDate = t2.MonthEndDate;

Update Table with auto incrementing dates in date column

I want to update a Table with auto incrementing dates in date column in Sql server 2005.
To label newly inserted rows, you could combine an identity column with a calculated field. For example:
declare #t table
(
dayNumber int identity,
Date as dateadd(day, dayNumber-1, '1970-01-01')
)
insert into #t default values
insert into #t default values
insert into #t default values
select * from #t
This prints:
dayNumber Date
1 1970-01-01 00:00:00.000
2 1970-01-02 00:00:00.000
3 1970-01-03 00:00:00.000
To update columns in an existing table with increasing dates, use row_number, like:
declare #t2 table (id int identity, name varchar(50), DateColumn datetime)
insert #t2 (name) values ('Maggie'), ('Tom'), ('Phillip'), ('Stephen')
update t2
set DateColumn = DATEADD(day, rn-1, '1970-01-01')
from #t2 t2
join (
select ROW_NUMBER() over (order by id) rn
, id
from #t2
) t2_numbered
on t2_numbered.id = t2.id
select * from #t2
This prints:
id name DateColumn
1 Maggie 1970-01-01 00:00:00.000
2 Tom 1970-01-02 00:00:00.000
3 Phillip 1970-01-03 00:00:00.000
4 Stephen 1970-01-04 00:00:00.000

SQL query for cumulative frequency of list of datetimes

I have a list of times in a database column (representing visits to a website).
I need to group them in intervals and then get a 'cumulative frequency' table of those dates.
For instance I might have:
9:01
9:04
9:11
9:13
9:22
9:24
9:28
and i want to convert that into
9:05 - 2
9:15 - 4
9:25 - 6
9:30 - 7
How can I do that? Can i even easily achieve this in SQL? I can quite easily do it in C#
create table accu_times (time_val datetime not null, constraint pk_accu_times primary key (time_val));
go
insert into accu_times values ('9:01');
insert into accu_times values ('9:05');
insert into accu_times values ('9:11');
insert into accu_times values ('9:13');
insert into accu_times values ('9:22');
insert into accu_times values ('9:24');
insert into accu_times values ('9:28');
go
select rounded_time,
(
select count(*)
from accu_times as at2
where at2.time_val <= rt.rounded_time
) as accu_count
from (
select distinct
dateadd(minute, round((datepart(minute, at.time_val) + 2)*2, -1)/2,
dateadd(hour, datepart(hour, at.time_val), 0)
) as rounded_time
from accu_times as at
) as rt
go
drop table accu_times
Results in:
rounded_time accu_count
----------------------- -----------
1900-01-01 09:05:00.000 2
1900-01-01 09:15:00.000 4
1900-01-01 09:25:00.000 6
1900-01-01 09:30:00.000 7
I should point out that based on the stated "intent" of the problem, to do analysis on visitor traffic - I wrote this statement to summarize the counts in uniform groups.
To do otherwise (as in the "example" groups) would be comparing the counts during a 5 minute interval to counts in a 10 minute interval - which doesn't make sense.
You have to grok to the "intent" of the user requirement, not the literal "reading" of it. :-)
create table #myDates
(
myDate datetime
);
go
insert into #myDates values ('10/02/2008 09:01:23');
insert into #myDates values ('10/02/2008 09:03:23');
insert into #myDates values ('10/02/2008 09:05:23');
insert into #myDates values ('10/02/2008 09:07:23');
insert into #myDates values ('10/02/2008 09:11:23');
insert into #myDates values ('10/02/2008 09:14:23');
insert into #myDates values ('10/02/2008 09:19:23');
insert into #myDates values ('10/02/2008 09:21:23');
insert into #myDates values ('10/02/2008 09:21:23');
insert into #myDates values ('10/02/2008 09:21:23');
insert into #myDates values ('10/02/2008 09:21:23');
insert into #myDates values ('10/02/2008 09:21:23');
insert into #myDates values ('10/02/2008 09:26:23');
insert into #myDates values ('10/02/2008 09:27:23');
insert into #myDates values ('10/02/2008 09:29:23');
go
declare #interval int;
set #interval = 10;
select
convert(varchar(5), dateadd(minute,#interval - datepart(minute, myDate) % #interval, myDate), 108) timeGroup,
count(*)
from
#myDates
group by
convert(varchar(5), dateadd(minute,#interval - datepart(minute, myDate) % #interval, myDate), 108)
retuns:
timeGroup
--------- -----------
09:10 4
09:20 3
09:30 8
ooh, way too complicated all of that stuff.
Normalise to seconds, divide by your bucket interval, truncate and remultiply:
select sec_to_time(floor(time_to_sec(d)/300)*300), count(*)
from d
group by sec_to_time(floor(time_to_sec(d)/300)*300)
Using Ron Savage's data, I get
+----------+----------+
| i | count(*) |
+----------+----------+
| 09:00:00 | 1 |
| 09:05:00 | 3 |
| 09:10:00 | 1 |
| 09:15:00 | 1 |
| 09:20:00 | 6 |
| 09:25:00 | 2 |
| 09:30:00 | 1 |
+----------+----------+
You may wish to use ceil() or round() instead of floor().
Update: for a table created with
create table d (
d datetime
);
Create a table periods describing the periods you wish to divide the day up into.
SELECT periods.name, count(time)
FROM periods, times
WHERE period.start <= times.time
AND times.time < period.end
GROUP BY periods.name
Create a table containing what intervals you want to be getting totals at then join the two tables together.
Such as:
time_entry.time_entry
-----------------------
2008-10-02 09:01:00.000
2008-10-02 09:04:00.000
2008-10-02 09:11:00.000
2008-10-02 09:13:00.000
2008-10-02 09:22:00.000
2008-10-02 09:24:00.000
2008-10-02 09:28:00.000
time_interval.time_end
-----------------------
2008-10-02 09:05:00.000
2008-10-02 09:15:00.000
2008-10-02 09:25:00.000
2008-10-02 09:30:00.000
SELECT
ti.time_end,
COUNT(*) AS 'interval_total'
FROM time_interval ti
INNER JOIN time_entry te
ON te.time_entry < ti.time_end
GROUP BY ti.time_end;
time_end interval_total
----------------------- -------------
2008-10-02 09:05:00.000 2
2008-10-02 09:15:00.000 4
2008-10-02 09:25:00.000 6
2008-10-02 09:30:00.000 7
If instead of wanting cumulative totals you wanted totals within a range, then you add a time_start column to the time_interval table and change the query to
SELECT
ti.time_end,
COUNT(*) AS 'interval_total'
FROM time_interval ti
INNER JOIN time_entry te
ON te.time_entry >= ti.time_start
AND te.time_entry < ti.time_end
GROUP BY ti.time_end;
This uses quite a few SQL tricks (SQL Server 2005):
CREATE TABLE [dbo].[stackoverflow_165571](
[visit] [datetime] NOT NULL
) ON [PRIMARY]
GO
;WITH buckets AS (
SELECT dateadd(mi, (1 + datediff(mi, 0, visit - 1 - dateadd(dd, 0, datediff(dd, 0, visit))) / 5) * 5, 0) AS visit_bucket
,COUNT(*) AS visit_count
FROM stackoverflow_165571
GROUP BY dateadd(mi, (1 + datediff(mi, 0, visit - 1 - dateadd(dd, 0, datediff(dd, 0, visit))) / 5) * 5, 0)
)
SELECT LEFT(CONVERT(varchar, l.visit_bucket, 8), 5) + ' - ' + CONVERT(varchar, SUM(r.visit_count))
FROM buckets l
LEFT JOIN buckets r
ON r.visit_bucket <= l.visit_bucket
GROUP BY l.visit_bucket
ORDER BY l.visit_bucket
Note that it puts all the times on the same day, and assumes they are in a datetime column. The only thing it doesn't do as your example does is strip the leading zeroes from the time representation.