Retrieving distinct values and a count on duplicates in SQL - sql

We have a table, lets call it "source-table", from where I retrieve these columns like this;
ANr, TNr, Type, IDate, EDate
1132173, 113615, Bogus, 2017-09-11 13:01:00, 2017-09-13 14:10:00
1132145, 184210, Triss, 2017-09-11 13:05:00, 2017-09-13 14:10:00
1131828, 259858, Bogus, 2017-09-11 13:11:00, 2017-09-13 14:10:00
1131844, 259858, Bogus, 2017-09-11 13:11:00, 2017-09-13 14:10:00
The above result is going to a new table called "export-table" and I then want to do a select from a third table, "info-table" but only with those just inserted in "export-table" and with a Count on how many duplicated entries if any (besides Anr which always is unique)
With the select mention above I want the following result:
Customernr, ANr, IDate, Type, Amount
703524, 1132173,2017-09-11 13:01:00, Bogus, 1
756899, 1132145,2017-09-11 13:05:00, Triss, 1
356658, 1131828,2017-09-11 13:11:00, Bogus, 2
Customernr comes from the "info-table", which has ANr as unique key.
As you can see the last two rows from "source-table" is identical besides Anr, but I need Anr later to get correct values from "info-table". It might work without Anr, if I can do a search on "info-table" with both Tnr and IDate, but I'll get duplicate entries there as well so not sure if that helps.
If someone wonder why the "export-table", then it is a kind of fail-safe so I wont export same stuff more than once.
I've been searching but since it is a bit complex question I haven't found a complete solution or any good hints.

how you did not put the customer, the best I can help you is
DECLARE #t table
(
ANr int
,TNr int
, Type varchar(10)
, IDate datetime
, EDate Datetime
)
insert #t (ANr, TNr, Type, IDate, EDate) values
(1132173, 113615, 'Bogus', '2017-09-11 13:01:00', '2017-09-13 14:10:00')
,(1132145, 184210, 'Triss', '2017-09-11 13:05:00', '2017-09-13 14:10:00')
,(1131828, 259858, 'Bogus', '2017-09-11 13:11:00', '2017-09-13 14:10:00')
,(1131844, 259858, 'Bogus', '2017-09-11 13:11:00', '2017-09-13 14:10:00')
Select Distinct
TNr
,IDate
,Type
,COUNT(1) OVER (PARTITION BY TNr, Type, IDATE)
from #t
Result
TNr IDate Type
----------- ----------------------- ---------- -----------
113615 2017-09-11 13:01:00.000 Bogus 1
184210 2017-09-11 13:05:00.000 Triss 1
259858 2017-09-11 13:11:00.000 Bogus 2

You can query as below:
Select top (1) with ties ANr, Idate, [Type], Count(*) over(partition by TNr, [Type], Idate) from
#sourcetable
order by row_number() over(partition by TNr, [Type], Idate order by Anr)
But not sure how you got Customernr
Output as below:
+---------+-------------------------+-------+--------+
| ANr | Idate | Type | Amount |
+---------+-------------------------+-------+--------+
| 1132173 | 2017-09-11 13:01:00.000 | Bogus | 1 |
| 1132145 | 2017-09-11 13:05:00.000 | Triss | 1 |
| 1131828 | 2017-09-11 13:11:00.000 | Bogus | 2 |
+---------+-------------------------+-------+--------+
You can use sub query for getting row_number as below
Select * from (
Select ANr, Idate, [Type], Amount = Count(*) over(partition by TNr, [Type], Idate)
,RowN = row_number() over(partition by TNr, [Type], Idate order by Anr)
from #sourcetable ) a
Where a.RowN = 1

DECLARE #t TABLE(ANr int, TNr int, Type NVARCHAR(100), IDate DATETIME, EDate DATETIME);
INSERT INTO #t VALUES
(1132173, 113615, 'Bogus', '2017-09-11 13:01:00', '2017-09-13 14:10:00')
,(1132145, 184210, 'Triss', '2017-09-11 13:05:00', '2017-09-13 14:10:00')
,(1131828, 259858, 'Bogus', '2017-09-11 13:11:00', '2017-09-13 14:10:00')
,(1131844, 259858, 'Bogus', '2017-09-11 13:11:00', '2017-09-13 14:10:00');
SELECT MIN(ANr) ANr, TNr, IDate, EDate, COUNT(*) AS Anz
FROM #t
GROUP BY TNr, IDate, EDate

Related

Dynamically Insert into Table A Based on Row_Num from Table B

I've condensed some data into TableB which looks like the following:
+========+==========+=========+
| AreaID | AreaName | Row_Num |
+========+==========+=========+
| 506 | BC-VanW | 1 |
+--------+----------+---------+
| 3899 | BC-VicS | 2 |
+--------+----------+---------+
| 1253 | AB-CalW | 3 |
+--------+----------+---------+
There are 2000 unique rows in total, Row_Num from 1 to 2000, and every AreaID in this table is naturally unique as well.
I now want to insert into a blank table, TableA, which has the following columns:
+========+==========+=========+============+===========+
| AreaID | StartDT | EndDT | MarketCode |Allocation |
+========+==========+=========+============+===========+
The insert statement I want to use is repeats everything except for the AreaID
I was attempting some things earlier and this is a basic look at what I have that I'm hoping Stackoverflow could help me expand on:
DECLARE #AreaID NVARCHAR(4)
SET #AreaID = (SELECT AreaID FROM TableB WHERE Row_Num = 1)
DECLARE #Sql NVARCHAR(MAX)
SET #Sql = N'
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation) VALUES ('+#AreaID+', ''2020-11-01 00:00:00.000'', ''2049-12-31 00:00:00.000'' , 31 , 25.00);
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation) VALUES ('+#AreaID+', ''2020-11-01 00:00:00.000'', ''2049-12-31 00:00:00.000'' , 38 , 60.00);
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation) VALUES ('+#AreaID+', ''2020-11-01 00:00:00.000'', ''2049-12-31 00:00:00.000'' , 39 , 15.00);
'
EXEC sp_executesql #Sql
GO
From here I would want it to 'loop' through the Row_Nums, once and once only, and run the full insert query above eventually doing it for all 2000 Row_Nums.
Of course, if there is a more efficient way please let me know and I will take a look.
Thanks!
I think you want a cross join with a fixed list of values:
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation)
SELECT b.AreaID, x.*
FROM tableB b
CROSS APPLY (VALUES
('2020-11-01 00:00:00.000', '2049-12-31 00:00:00.000', 31, 25.00),
('2020-11-01 00:00:00.000', '2049-12-31 00:00:00.000', 38, 60.00),
('2020-11-01 00:00:00.000', '2049-12-31 00:00:00.000', 39, 15.00)
) x(StartDt, EndDt, MarketCode, Allocation)
If the date range is always the same, this can be simplified a little:
INSERT INTO TableA (AreaID, StartDt, EndDt, MarketCode, Allocation)
SELECT b.AreaID, '2020-11-01 00:00:00.000', '2049-12-31 00:00:00.000', x.*
FROM tableB b
CROSS APPLY (VALUES
(31 , 25.00),
(38 , 60.00),
(39 , 15.00)
) x(MarketCode, Allocation)

Split a record to multiple rows of record

I have table as below
Master Table
ID Name
1 Bubble
Child Table
ID MasterTableID StartDate EndDate Qty UnitMeasurement
1 1 1/2/2019 1/6/2019 1000 sqft
2 1 1/2/2019 1/4/2019 3000 sqft
I need to select the record above and show it in 5 rows since 1/2 - 1/6 were 5 months.
Date Qty
1/2/2019 200
1/3/2019 200
1/4/2019 200
1/5/2019 200
1/6/2019 200
Second row record to 3 rows record
Date Qty
1/2/2019 1000
1/3/2019 1000
1/4/2019 1000
I'm using SQL Server.
May I know it is possible to do so?
you can use Recursively + CTE and filter using inner join on id
CREATE TABLE T
([ID] int, [MasterTableID] int, [StartDate] datetime, [EndDate] datetime, [Qty] int, [UnitMeasurement] varchar(4))
;
INSERT INTO T
([ID], [MasterTableID], [StartDate], [EndDate], [Qty], [UnitMeasurement])
VALUES
(1, 1, '2019-01-02 00:00:00', '2019-01-06 00:00:00', 1000, 'sqft'),
(2, 1, '2019-01-02 00:00:00', '2019-01-04 00:00:00', 3000, 'sqft')
;
GO
2 rows affected
with cte as (
select [EndDate] as [Date],ID,datediff(day,[StartDate], [EndDate]) diff , [Qty] / (datediff(day,[StartDate], [EndDate]) + 1) as qty
from T
union all
select dateadd(day,-1,[Date]) [Date],T1.ID,T2.diff - 1 as diff,T2.qty
from T T1
inner join cte T2 on T1.ID = T2.ID
where diff >0
)
select ID,[Date],qty
from cte
order by ID,[Date]
GO
ID | Date | qty
-: | :------------------ | ---:
1 | 02/01/2019 00:00:00 | 200
1 | 03/01/2019 00:00:00 | 200
1 | 04/01/2019 00:00:00 | 200
1 | 05/01/2019 00:00:00 | 200
1 | 06/01/2019 00:00:00 | 200
2 | 02/01/2019 00:00:00 | 1000
2 | 03/01/2019 00:00:00 | 1000
2 | 04/01/2019 00:00:00 | 1000
db<>fiddle here
This is achievable using cte. since your dateformat is ddMMyyy, we need to convert this to MMddyyy so we can use dateadd(month...
CREATE TABLE #Temp
(id int, [StartDate] varchar(30), [EndDate] varchar(30), [Qty] int, [UnitMeasurement] varchar(4))
;
INSERT INTO #Temp
(id, [StartDate], [EndDate], [Qty], [UnitMeasurement])
VALUES
(1, '1/2/2019', '1/6/2019', 1000, 'sqft'),
(2, '1/2/2019', '1/4/2019', 3000, 'sqft')
;
GO
with cte as
(
Select id, cast(convert(varchar
, convert(datetime, [StartDate], 103), 101) as date) as startdate
, cast(convert(varchar
, convert(datetime, [EndDate], 103), 101) as date) as enddate
, [Qty]
, 1 as ctr from #Temp
union all
Select id, dateadd(month, 1, startdate), enddate, qty, ctr + 1
From cte
Where startdate < enddate
)
Select t1.id, qty/t2.ct, startdate from cte t1
cross apply (select count(1) ct, id from cte group by id) t2
where t2.id = t1.id
order by t1.id asc
Option (MaxRecursion 0)
drop table #Temp
output:
try like below for generating date
DECLARE #StartDate DATE = '1/2/2019'
, #EndDate DATE = '1/6/2019'
SELECT DATEADD(DAY, nbr - 1, #StartDate)
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS Nbr
FROM sys.columns c
) nbrs
WHERE nbr - 1 <= DATEDIFF(DAY, #StartDate, #EndDate)
or you can use recursion
Declare #FromDate Date = '1/2/2019',
#ToDate Date = '1/6/2019'
;With DateCte (Date) As
(
Select #FromDate Union All
Select DateAdd(Day, 1, Date)
From DateCte
Where Date <= #ToDate
)
Select Date
From DateCte
Option (MaxRecursion 0)

Date range with minimum and maximum dates from dataset having records with continuous date range

I have a dataset with id ,Status and date range of employees.
The input dataset given below are the details of one employee.
The date ranges in the records are continuous(in exact order) such that startdate of second row will be the next date of enddate of first row.
If an employee takes leave continuously for different months, then the table is storing the info with date range as separated for different months.
For example: In the input set, the employee has taken Sick leave from '16-10-2016' to '31-12-2016' and joined back on '1-1-2017'.
So there are 3 records for this item but the dates are continuous.
In the output I need this as one record as shown in the expected output dataset.
INPUT
Id Status StartDate EndDate
1 Active 1-9-2007 15-10-2016
1 Sick 16-10-2016 31-10-2016
1 Sick 1-11-2016 30-11-2016
1 Sick 1-12-2016 31-12-2016
1 Active 1-1-2017 4-2-2017
1 Unpaid 5-2-2017 9-2-2017
1 Active 10-2-2017 11-2-2017
1 Unpaid 12-2-2017 28-2-2017
1 Unpaid 1-3-2017 31-3-2017
1 Unpaid 1-4-2017 30-4-2017
1 Active 1-5-2017 13-10-2017
1 Sick 14-10-2017 11-11-2017
1 Active 12-11-2017 NULL
EXPECTED OUTPUT
Id Status StartDate EndDate
1 Active 1-9-2007 15-10-2016
1 Sick 16-10-2016 31-12-2016
1 Active 1-1-2017 4-2-2017
1 Unpaid 5-2-2017 9-2-2017
1 Active 10-2-2017 11-2-2017
1 Unpaid 12-2-2017 30-4-2017
1 Active 1-5-2017 13-10-2017
1 Sick 14-10-2017 11-11-2017
1 Active 12-11-2017 NULL
I can't take min(startdate) and max(EndDate) group by id,status because if the same employee has taken another Sick leave then that end date ('11-11-2017' in the example) will come as the End date.
can anyone help me with the query in SQL server 2014?
It suddenly hit me that this is basically a gaps and islands problem - so I've completely changed my solution.
For this solution to work, the dates does not have to be consecutive.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
Id int,
Status varchar(10),
StartDate date,
EndDate date
);
SET DATEFORMAT DMY; -- This is needed because how you specified your dates.
INSERT INTO #T (Id, Status, StartDate, EndDate) VALUES
(1, 'Active', '1-9-2007', '15-10-2016'),
(1, 'Sick', '16-10-2016', '31-10-2016'),
(1, 'Sick', '1-11-2016', '30-11-2016'),
(1, 'Sick', '1-12-2016', '31-12-2016'),
(1, 'Active', '1-1-2017', '4-2-2017'),
(1, 'Unpaid', '5-2-2017', '9-2-2017'),
(1, 'Active', '10-2-2017', '11-2-2017'),
(1, 'Unpaid', '12-2-2017', '28-2-2017'),
(1, 'Unpaid', '1-3-2017', '31-3-2017'),
(1, 'Unpaid', '1-4-2017', '30-4-2017'),
(1, 'Active', '1-5-2017', '13-10-2017'),
(1, 'Sick', '14-10-2017', '11-11-2017'),
(1, 'Active', '12-11-2017', NULL);
The (new) common table expression:
;WITH CTE AS
(
SELECT Id,
Status,
StartDate,
EndDate,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY StartDate)
- ROW_NUMBER() OVER(PARTITION BY Id, Status ORDER BY StartDate) As IslandId,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY StartDate DESC)
- ROW_NUMBER() OVER(PARTITION BY Id, Status ORDER BY StartDate DESC) As ReverseIslandId
FROM #T
)
The (new) query:
SELECT DISTINCT Id,
Status,
MIN(StartDate) OVER(PARTITION BY IslandId, ReverseIslandId) As StartDate,
NULLIF(MAX(ISNULL(EndDate, '9999-12-31')) OVER(PARTITION BY IslandId, ReverseIslandId), '9999-12-31') As EndDate
FROM CTE
ORDER BY StartDate
(new) Results:
Id Status StartDate EndDate
1 Active 01.09.2007 15.10.2016
1 Sick 16.10.2016 31.12.2016
1 Active 01.01.2017 04.02.2017
1 Unpaid 05.02.2017 09.02.2017
1 Active 10.02.2017 11.02.2017
1 Unpaid 12.02.2017 30.04.2017
1 Active 01.05.2017 13.10.2017
1 Sick 14.10.2017 11.11.2017
1 Active 12.11.2017 NULL
You can see a live demo on rextester.
Please note that string representation of dates in SQL should be acccording to ISO 8601 - meaning either yyyy-MM-dd or yyyyMMdd as it's unambiguous and will always be interpreted correctly by SQL Server.
It's an example of GROUPING AND WINDOW.
First you set a reset point for each Status
Sum to set a group
Then get max/min dates of each group.
;with x as
(
select Id, Status, StartDate, EndDate,
iif (lag(Status) over (order by Id, StartDate) = Status, null, 1) rst
from emp
), y as
(
select Id, Status, StartDate, EndDate,
sum(rst) over (order by Id, StartDate) grp
from x
)
select Id,
MIN(Status) as Status,
MIN(StartDate) StartDate,
MAX(EndDate) EndDate
from y
group by Id, grp
order by Id, grp
GO
Id | Status | StartDate | EndDate
-: | :----- | :------------------ | :------------------
1 | Active | 01/09/2007 00:00:00 | 15/10/2016 00:00:00
1 | Sick | 16/10/2016 00:00:00 | 31/12/2016 00:00:00
1 | Active | 01/01/2017 00:00:00 | 04/02/2017 00:00:00
1 | Unpaid | 05/02/2017 00:00:00 | 09/02/2017 00:00:00
1 | Active | 10/02/2017 00:00:00 | 11/02/2017 00:00:00
1 | Unpaid | 12/02/2017 00:00:00 | 30/04/2017 00:00:00
1 | Active | 01/05/2017 00:00:00 | 13/10/2017 00:00:00
1 | Sick | 14/10/2017 00:00:00 | 11/11/2017 00:00:00
1 | Active | 12/11/2017 00:00:00 | null
dbfiddle here
Here's an alternative answer that doesn't use LAG.
First I need to take a copy of your test data:
DECLARE #table TABLE (Id INT, [Status] VARCHAR(50), StartDate DATE, EndDate DATE);
INSERT INTO #table SELECT 1, 'Active', '20070901', '20161015';
INSERT INTO #table SELECT 1, 'Sick', '20161016', '20161031';
INSERT INTO #table SELECT 1, 'Sick', '20161101', '20161130';
INSERT INTO #table SELECT 1, 'Sick', '20161201', '20161231';
INSERT INTO #table SELECT 1, 'Active', '20170101', '20170204';
INSERT INTO #table SELECT 1, 'Unpaid', '20170205', '20170209';
INSERT INTO #table SELECT 1, 'Active', '20170210', '20170211';
INSERT INTO #table SELECT 1, 'Unpaid', '20170212', '20170228';
INSERT INTO #table SELECT 1, 'Unpaid', '20170301', '20170331';
INSERT INTO #table SELECT 1, 'Unpaid', '20170401', '20170430';
INSERT INTO #table SELECT 1, 'Active', '20170501', '20171013';
INSERT INTO #table SELECT 1, 'Sick', '20171014', '20171111';
INSERT INTO #table SELECT 1, 'Active', '20171112', NULL;
Then the query is:
WITH add_order AS (
SELECT
*,
ROW_NUMBER() OVER (ORDER BY StartDate) AS order_id
FROM
#table),
links AS (
SELECT
a1.Id,
a1.[Status],
a1.order_id,
MIN(a1.order_id) AS start_order_id,
MAX(ISNULL(a2.order_id, a1.order_id)) AS end_order_id,
MIN(a1.StartDate) AS StartDate,
MAX(ISNULL(a2.EndDate, a1.EndDate)) AS EndDate
FROM
add_order a1
LEFT JOIN add_order a2 ON a2.Id = a1.Id AND a2.[Status] = a1.[Status] AND a2.order_id = a1.order_id + 1 AND a2.StartDate = DATEADD(DAY, 1, a1.EndDate)
GROUP BY
a1.Id,
a1.[Status],
a1.order_id),
merged AS (
SELECT
l1.Id,
l1.[Status],
l1.[StartDate],
ISNULL(l2.EndDate, l1.EndDate) AS EndDate,
ROW_NUMBER() OVER (PARTITION BY l1.Id, l1.[Status], ISNULL(l2.EndDate, l1.EndDate) ORDER BY l1.order_id) AS link_id
FROM
links l1
LEFT JOIN links l2 ON l2.order_id = l1.end_order_id)
SELECT
Id,
[Status],
StartDate,
EndDate
FROM
merged
WHERE
link_id = 1
ORDER BY
StartDate;
Results are:
Id Status StartDate EndDate
1 Active 2007-09-01 2016-10-15
1 Sick 2016-10-16 2016-12-31
1 Active 2017-01-01 2017-02-04
1 Unpaid 2017-02-05 2017-02-09
1 Active 2017-02-10 2017-02-11
1 Unpaid 2017-02-12 2017-04-30
1 Active 2017-05-01 2017-10-13
1 Sick 2017-10-14 2017-11-11
1 Active 2017-11-12 NULL
How does it work? First I add a sequence number, to assist with merging contiguous rows together. Then I determine the rows that can be merged together, add a number to identify the first row in each set that can be merged, and finally pick the first rows out of the final CTE. Note that I also have to handle rows that can't be merged, hence the LEFT JOINs and ISNULL statements.
Just for interest, this is what the output from the final CTE looks like, before I filter out all but the rows with a link_id of 1:
Id Status StartDate EndDate link_id
1 Active 2007-09-01 2016-10-15 1
1 Sick 2016-10-16 2016-12-31 1
1 Sick 2016-11-01 2016-12-31 2
1 Sick 2016-12-01 2016-12-31 3
1 Active 2017-01-01 2017-02-04 1
1 Unpaid 2017-02-05 2017-02-09 1
1 Active 2017-02-10 2017-02-11 1
1 Unpaid 2017-02-12 2017-04-30 1
1 Unpaid 2017-03-01 2017-04-30 2
1 Unpaid 2017-04-01 2017-04-30 3
1 Active 2017-05-01 2017-10-13 1
1 Sick 2017-10-14 2017-11-11 1
1 Active 2017-11-12 NULL 1
You could use lag() and lead() function together to check the previous and next status
WITH CTE AS
(
select *,
COALESCE(LEAD(status) OVER(ORDER BY (select 1)), '0') Nstatus,
COALESCE(LAG(status) OVER(ORDER BY (select 1)), '0') Pstatus
from table
)
SELECT * FROM CTE
WHERE (status <> Nstatus AND status <> Pstatus) OR
(status <> Pstatus)

Select highest value outside of datetime range when grouped

I have a table containing four columns:
Id (uniqueidentifier),
Name (varchar),
StartDateTime (datetime),
EndDateTime (datetime)
And two input parameters:
#StartDateTime (datetime)
#EndDateTime (datetime)
My query currently looks like the following:
SELECT
[Id],
[Name],
MIN([StartDateTime]),
MAX([EndDateTime]),
FROM
[Table]
WHERE
[StartDateTime] BETWEEN #StartDateTime AND #EndDateTime
GROUP BY
[Id],
[Name],
DATEADD(DD, DATEDIFF(DD, 0, [StartDateTime]), 0)
Is there a way to somehow also select the maximum EndDateTime in the table which is less than the MIN([StartDateTime]), regardless of the way the table is grouped? E.g. for the following set of data, if #StartDateTime = '2016-06-01' and #EndDateTime = '2016-06-02', I would to have a column returned in my query that retrieves 2015-05-31 09:07:17.000 as the EndDateTime for the ID of 1, 2015-05-31 09:44:00.000 as the EndDateTime for the ID of 2, etc, since it is the highest EndDateTime before the selected MIN([StartDateTime])
ID | StartDateTime | EndDateTime
---------------------------------------------------------
1 | 2015-05-31 08:44:59.000 | 2015-05-31 09:07:17.000
2 | 2015-05-31 09:12:06.000 | 2015-05-31 09:44:00.000
3 | 2015-05-31 13:25:47.000 | 2015-05-31 13:34:34.000
4 | 2015-05-31 14:15:54.000 | 2015-05-31 14:24:23.000
1 | 2015-06-01 06:08:47.000 | 2015-06-01 06:10:58.000
2 | 2015-06-01 06:12:05.000 | 2015-06-01 07:24:11.000
3 | 2015-06-01 12:54:53.000 | 2015-06-01 12:55:34.000
4 | 2015-06-01 13:32:18.000 | 2015-06-01 13:33:05.000
Check if it helps.
select t.Id, t.Name, minStart, MAX(t.EndDateTime) from #temp t inner join
(select Id, Name, MIN(StartDateTime) minStart from #temp t1 group by Id, Name,
DATEADD(DD, DATEDIFF(DD, 0, starttime), 0))t1
ON t1.minStart = t.StartDateTime
where t.EndTime < t1.minStart
GROUP BY t.Id, t.Name, minStart
Use ranking to get what you need. This is a working example below.
create table #Temp
(
Id int,
Name varchar(10),
StartDate DateTime,
EndDate DateTime
)
insert into #Temp (Id, StartDate, EndDate)
values
(1 , '2015-05-31 08:44:59.000' , '2015-05-31 09:07:17.000' ),
(2 , '2015-05-31 09:12:06.000' , '2015-05-31 09:44:00.000' ),
(3 , '2015-05-31 13:25:47.000' , '2015-05-31 13:34:34.000' ),
(4 , '2015-05-31 14:15:54.000' , '2015-05-31 14:24:23.000' ),
(1 , '2015-06-01 06:08:47.000' , '2015-06-01 06:10:58.000' ),
(2 , '2015-06-01 06:12:05.000' , '2015-06-01 07:24:11.000' ),
(3 , '2015-06-01 12:54:53.000' , '2015-06-01 12:55:34.000' ),
(4 , '2015-06-01 13:32:18.000' , '2015-06-01 13:33:05.000' )
select s.Id, S.EndDate
from (
select ID, StartDate, EndDate, Rank() Over(Partition By Id Order By StartDate asc) as xrank
from #Temp
) s
where s.xrank = 1
drop table #temp
Do not restrict data with #StartDateTime till lag() is computed
DECLARE #StartDateTime DATETIME = '2015-06-01' ,
#EndDateTime DATETIME = '2015-06-02';
SELECT ID,sd,ed,ped
FROM (
SELECT [Id],
sd = MIN([StartDateTime]),
ed = MAX([EndDateTime]),
ped = LAG(MAX([EndDateTime])) OVER(PARTITION BY id ORDER BY MIN([StartDateTime]))
FROM(
-- sample data
VALUES (1, CAST('2015-05-31 08:44:59.000' AS DATETIME),CAST('2015-05-31 09:07:17.000' AS DATETIME))
,(2,'2015-05-31 09:12:06.000','2015-05-31 09:44:00.000')
,(3,'2015-05-31 13:25:47.000','2015-05-31 13:34:34.000')
,(4,'2015-05-31 14:15:54.000','2015-05-31 14:24:23.000')
,(1,'2015-06-01 06:08:47.000','2015-06-01 06:10:58.000')
,(2,'2015-06-01 06:12:05.000','2015-06-01 07:24:11.000')
,(3,'2015-06-01 12:54:53.000','2015-06-01 12:55:34.000')
,(4,'2015-06-01 13:32:18.000','2015-06-01 13:33:05.000')
) t(id,StartDateTime,EndDateTime)
WHERE [StartDateTime] <= #EndDateTime
GROUP BY
[Id],
DATEADD(DD, DATEDIFF(DD, 0, [StartDateTime]), 0)
) x
WHERE sd >= #StartDateTime;

Selecting the second (middle) row between MIN & MAX values in SQL Server

I have the following table:
TicketNumber CallDate
--------------------------------------------
101 10/09/2015 3:15:43 PM
101 10/09/2015 3:45:43 PM
101 11/19/2015 2:23:09 PM
I want to select the min date, the middle date and the max date. It is easy to get the first and last dates using MIN and MAX. But how to SELECT (get) the second date?
SELECT
TicketNumber
, MIN(CallDate) CallDate1
, MAX(CallDate) CallDate3
, COUNT(TicketNumber) [Count]
FROM Table1
WHERE -(conditions)-
GROUP BY TicketNumber
HAVING COUNT(TicketNumber)=3
Between MIN & MAX dates in the SELECT statement I want the second row date.
The expected output should be:
TicketNumber CallDate1 CallDate2 CallDate3 Count
------------------------------------------------------------------------------------------
101 10/9/2015 3:15:43 PM 10/9/2015 3:45:43 PM 11/19/2015 2:23:09 PM 3
Here is one possible variant. At first number and count all rows, then filter only those TicketNumbers that have three tickets and PIVOT result.
SQL Fiddle
Sample data
DECLARE #Tickets TABLE (TicketNumber int, CallDate datetime2(0));
INSERT INTO #Tickets (TicketNumber, CallDate) VALUES
(101, '2015-10-09 03:15:43'),
(101, '2015-10-09 03:45:43'),
(101, '2015-11-19 02:23:09'),
(102, '2015-11-20 02:23:09'),
(102, '2015-11-19 02:23:09'),
(102, '2015-11-21 02:23:09'),
(103, '2015-11-10 02:23:09'),
(103, '2015-11-19 02:23:09'),
(104, '2015-11-11 02:23:09'),
(104, '2015-11-01 02:23:09'),
(104, '2015-11-21 02:23:09'),
(104, '2015-11-30 02:23:09');
Query
WITH
CTE
AS
(
SELECT
TicketNumber
,CallDate
,ROW_NUMBER() OVER (PARTITION BY TicketNumber ORDER BY CallDate) AS rn
,COUNT(*) OVER (PARTITION BY TicketNumber) AS cnt
FROM
#Tickets AS T
)
SELECT
P.TicketNumber
,[1] AS CallDate1
,[2] AS CallDate2
,[3] AS CallDate3
,cnt
FROM
CTE
PIVOT (MIN(CTE.CallDate) FOR rn IN ([1], [2], [3])) AS P
WHERE cnt = 3
ORDER BY P.TicketNumber;
Result
+--------------+---------------------+---------------------+---------------------+-----+
| TicketNumber | CallDate1 | CallDate2 | CallDate3 | cnt |
+--------------+---------------------+---------------------+---------------------+-----+
| 101 | 2015-10-09 03:15:43 | 2015-10-09 03:45:43 | 2015-11-19 02:23:09 | 3 |
| 102 | 2015-11-19 02:23:09 | 2015-11-20 02:23:09 | 2015-11-21 02:23:09 | 3 |
+--------------+---------------------+---------------------+---------------------+-----+
This can be achieved using table JOINS.
SELECT t1.TicketNumber, t2.CallDate1, t1.CallDate AS CallDate2, t2.CallDate3, t2.Count
FROM tickets AS t1
JOIN (
SELECT TicketNumber, MIN(CallDate) AS CallDate1, MAX(CallDate) AS CallDate3,
COUNT(TicketNumber) AS Count
FROM tickets
GROUP BY TicketNumber
HAVING COUNT(TicketNumber)=3
) AS t2
ON t1.TicketNumber = t2.TicketNumber
WHERE t1.CallDate > t2.CallDate1
AND t1.CallDate < t2.CallDate3
Working Fiddle