SQL Server query return next date with an event - sql

Basic SQL question but I have a mind blank. I have a table with the following setup:
date eventType
-----------------------
01/01/2016 0
02/01/2016 0
03/01/2016 2
03/01/2016 2
04/01/2016 6
04/01/2016 6
04/01/2016 6
04/01/2016 6
05/01/2016 0
06/01/2016 ...
I want to return the "next set of events where eventType<>0"
So, if "today" was 01/01/2016, the query would return:
03/01/2016 2
03/01/2016 2
If "today" was 03/01/2016, the query would return:
04/01/2016 6
04/01/2016 6
04/01/2016 6
04/01/2016 6
Etc.
Many thanks

Hmmm. I think this may be a bit trickier than it seems. This does what you want for the data in the question:
select e.*
from events e cross join
(select top 1 eventType
from events
where date > getdate() and eventType <> 0
order by date
) as nexte
where e.date > getdate() and
e.eventType = nexte.eventType;
Or, perhaps a better fit:
select e.*
from events e cross join
(select top (1) e.*
from events
where date > getdate() and eventType <> 0
order by date
) as nexte
where e.date > nexte.date and
e.eventType = nexte.eventType;
Or, more simply:
select top (1) with ties e.*
from events e
where date > getdate() and eventType <> 0
order by date, eventType

I have a different solution, check this:
DECLARE #dtEventType DATE = '20160101'
DECLARE #table TABLE ( cDate DATE , eventType TINYINT )
INSERT INTO #table
VALUES( '20160101' , 0 )
, ( '20160102' , 0 )
, ( '20160103' , 2 )
, ( '20160103' , 2 )
, ( '20160104' , 6 )
, ( '20160104' , 6 )
, ( '20160104' , 6 )
, ( '20160104' , 6 )
, ( '20160105' , 0 )
SELECT *
FROM #table L
WHERE cDate = (
SELECT MIN( cDate ) AS mnDate
FROM #table
WHERE eventType <> 0
AND cDate > #dtEventType
)
But I liked the #GordonLiff's 3rd solution .

Maybe this will work:
SELECT eventDate, event
FROM events
WHERE eventDayte > GETDATE()+1 -- limit here to datePart date to avoid confusion with time as this can lead to issues
-- we should provide limit here to avoid return all future events
AND eventDate <= GETDATE()+2
AND eventType<>0

Related

Overlapping between first record enddate and next record start date in SQL Server

I have the below kind of data and I need below kind of output.
Input:
id startdate enddate
1 21/01/2019 23/01/2019
1 23/01/2019 24/01/2019
1 24/01/2029 27/01/2019
1 29/01/2019 02/02/2019
Output:
id startdate enddate
1 21/01/2019 27/01/2019
1 29/01/2019 02/02/2019
We need to use the logic of matching the first record enddate and nth record startdate.
This is a gaps-and-islands problem, where you want to group together "adjacent" dates. Here is one approach using window functions: the idea is to compare the current start date to the end date of the "previous" row, and use a window sum to define the groups:
select id, min(startdate) startdate, max(enddate) enddate
from (
select t.*,
sum(case when startdate = lag_enddate then 0 else 1 end) over(partition by id order by startdate) grp
from (
select t.*,
lag(enddate) over(partition by id order by startdate) lag_enddate
from mytable t
) t
) t
group by id, grp
Demo on DB Fiddle - with credits to Sander for creating the DDL statements in the first place:
id | startdate | enddate
-: | :--------- | :---------
1 | 2019-01-21 | 2019-01-27
1 | 2019-01-29 | 2019-02-02
have a look at
NEXT VALUE FOR method, works 2016 and later
Use a CTE or subquery (works in 2008) where you join on your own table using the previous value as a join. Here a sample script I use showing backup growth
declare #backupType char(1)
, #DatabaseName sysname
set #DatabaseName = db_name() --> Name of current database, null for all databaseson server
set #backupType ='D' /* valid options are:
D = Database
I = Database Differential
L = Log
F = File or Filegroup
G = File Differential
P = Partial
Q = Partial Differential
*/
select backup_start_date
, backup_finish_date
, DurationSec
, database_name,backup_size
, PreviouseBackupSize
, backup_size-PreviouseBackupSize as growth
,KbSec= format(KbSec,'N2')
FROM (
select backup_start_date
, backup_finish_date
, datediff(second,backup_start_date,b.backup_finish_date) as DurationSec
, b.database_name
, b.backup_size/1024./1024. as backup_size
,case when datediff(second,backup_start_date,b.backup_finish_date) >0
then ( b.backup_size/1024.)/datediff(second,backup_start_date,b.backup_finish_date)
else 0 end as KbSec
-- , b.compressed_backup_size
, (
select top (1) p.backup_size/1024./1024.
from msdb.dbo.backupset p
where p.database_name = b.database_name
and p.database_backup_lsn< b.database_backup_lsn
and type=#backupType
order by p.database_backup_lsn desc
) as PreviouseBackupSize
from msdb.dbo.backupset as b
where #DatabaseName IS NULL OR database_name =#DatabaseName
and type=#backupType
)as A
order by backup_start_date desc
using a "cursor local fast_forward" to loop over the data on a row-by-row and use a temporary table where you store & compaire prev value
Here is a solution with common table expressions that could work.
Sample data
create table data
(
id int,
startdate date,
enddate date
);
insert into data (id, startdate, enddate) values
(1, '2019-01-21', '2019-01-23'),
(1, '2019-01-23', '2019-01-24'),
(1, '2019-01-24', '2019-01-27'),
(1, '2019-01-29', '2019-02-02');
Solution
-- determine start dates
with cte_start as
(
select s.id,
s.startdate
from data s
where not exists ( select 'x'
from data e
where e.id = s.id
and e.enddate = s.startdate )
),
-- determine date boundaries
cte_startnext as
(
select s.id,
s.startdate,
lead(s.startdate) over (partition by s.id order by s.startdate) as startdate_next
from cte_start s
)
-- determine periods
select sn.id,
sn.startdate,
e.enddate
from cte_startnext sn
cross apply ( select top 1 e.enddate
from data e
where e.id = sn.id
and e.startdate >= sn.startdate
and (e.startdate < sn.startdate_next or sn.startdate_next is null)
order by e.enddate desc ) e
order by sn.id,
sn.startdate;
Result
id startdate enddate
-- ---------- ----------
1 2019-01-21 2019-01-27
1 2019-01-29 2019-02-02
Fiddle to see build up of solution and intermediate CTE results.

Fill up date gap by month

I have table of products and their sales quantity in months.
Product Month Qty
A 2018-01-01 5
A 2018-02-01 3
A 2018-05-01 5
B 2018-08-01 10
B 2018-10-01 12
...
I'd like to first fill in the data gap between each product's min and max dates like below:
Product Month Qty
A 2018-01-01 5
A 2018-02-01 3
A 2018-03-01 0
A 2018-04-01 0
A 2018-05-01 5
B 2018-08-01 10
B 2018-09-01 0
B 2018-10-01 12
...
Then I would need to perform an accumulation of each product's sales quantity by month.
Product Month total_Qty
A 2018-01-01 5
A 2018-02-01 8
A 2018-03-01 8
A 2018-04-01 8
A 2018-05-01 13
B 2018-08-01 10
B 2018-09-01 10
B 2018-10-01 22
...
I fumbled over the "cross join" clause, however it seems to generate some unexpected results for me. Could someone help to give a hint how I can achieve this in SQL?
Thanks a lot in advance.
I think a recursive CTE is a simple way to do this. The code is just:
with cte as (
select product, min(mon) as mon, max(mon) as end_mon
from t
group by product
union all
select product, dateadd(month, 1, mon), end_mon
from cte
where mon < end_mon
)
select cte.product, cte.mon, coalesce(qty, 0) as qty
from cte left join
t
on t.product = cte.product and t.mon = cte.mon;
Here is a db<>fiddle.
Hi i think this example can help you and perform what you excepted :
CREATE TABLE #MyTable
(Product varchar(10),
ProductMonth DATETIME,
Qty int
);
GO
CREATE TABLE #MyTableTempDate
(
FullMonth DATETIME
);
GO
INSERT INTO #MyTable
SELECT 'A', '2019-01-01', 214
UNION
SELECT 'A', '2019-02-01', 4
UNION
SELECT 'A', '2019-03-01', 50
UNION
SELECT 'B', '2019-01-01', 214
UNION
SELECT 'B', '2019-02-01', 10
UNION
SELECT 'C', '2019-04-01', 150
INSERT INTO #MyTableTempDate
SELECT '2019-01-01'
UNION
SELECT '2019-02-01'
UNION
SELECT '2019-03-01'
UNION
SELECT '2019-04-01'
UNION
SELECT '2019-05-01'
UNION
SELECT '2019-06-01'
UNION
SELECT '2019-07-01';
------------- FOR NEWER SQL SERVER VERSION > 2005
WITH MyCTE AS
(
SELECT T.Product, T.ProductMonth AS 'MMonth', T.Qty
FROM #MyTable T
UNION
SELECT T.Product, TD.FullMonth AS 'MMonth', 0 AS 'Qty'
FROM #MyTable T, #MyTableTempDate TD
WHERE NOT EXISTS (SELECT 1 FROM #MyTable TT WHERE TT.Product = T.Product AND TD.FullMonth = TT.ProductMonth)
)
-- SELECT * FROM MyCTE;
SELECT Product, MMonth, Qty, SUM( Qty) OVER(PARTITION BY Product ORDER BY Product
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as 'TotalQty'
FROM MyCTE
ORDER BY Product, MMonth ASC;
DROP TABLE #MyTable
DROP TABLE #MyTableTempDate
I have other way to perform this in lower SQL Server Version (like 2005 and lower)
It's a SELECT on SELECT if it's your case let me know and i provide some other example.
You can create the months with a recursive CTE
DECLARE #MyTable TABLE
(
ProductID CHAR(1),
Date DATE,
Amount INT
)
INSERT INTO #MyTable
VALUES
('A','2018-01-01', 5),
('A','2018-02-01', 3),
('A','2018-05-01', 5),
('B','2018-08-01', 10),
('B','2018-10-01', 12)
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SELECT #StartDate = MIN(Date), #EndDate = MAX(Date) FROM #MyTable
;WITH dates AS (
SELECT #StartDate AS Date
UNION ALL
SELECT DATEADD(Month, 1, Date)
FROM dates
WHERE Date < #EndDate
)
SELECT A.ProductID, d.Date, COALESCE(Amount,0) AS Amount, COALESCE(SUM(Amount) OVER(PARTITION BY A.ProductID ORDER BY A.ProductID, d.Date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),0) AS Total
FROM
(
SELECT ProductID, MIN(date) as DateStart, MAX(date) as DateEnd
FROM #MyTable
GROUP BY ProductID -- As I read in your comments that you need different min and max dates per product
) A
JOIN dates d ON d.Date >= A.DateStart AND d.Date <= A.DateEnd
LEFT JOIN #MyTable T ON A.ProductID = T.ProductID AND T.Date = d.Date
ORDER BY A.ProductID, d.Date
Try this below
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
;WITH CTE(Product,[Month],Qty)
AS
(
SELECT 'A','2018-01-01', 5 UNION ALL
SELECT 'A','2018-02-01', 3 UNION ALL
SELECT 'A','2018-05-01', 5 UNION ALL
SELECT 'B','2018-08-01', 10 UNION ALL
SELECT 'D','2018-10-01', 12
)
SELECT ct.Product,[MonthDays],ct.Qty
INTO #Temp
FROM
(
SELECT c.Product,[Month],
ISNULL(Qty,0) AS Qty
FROM CTE c
)ct
RIGHT JOIN
(
SELECT -- This code is to get month data
CONVERT(VARCHAR(10),'2018-'+ RIGHT('00'+CAST(MONTH(DATEADD(MM, s.number, CONVERT(DATETIME, 0)))AS VARCHAR),2) +'-01',120) AS [MonthDays]
FROM master.dbo.spt_values s
WHERE [type] = 'P' AND s.number BETWEEN 0 AND 11
)DT
ON dt.[MonthDays] = ct.[Month]
SELECT
MAX(Product)OVER(ORDER BY [MonthDays])AS Product,
[MonthDays],
ISNULL(Qty,0) Qty,
SUM(ISNULL(Qty,0))OVER(ORDER BY [MonthDays]) As SumQty
FROM #Temp
Result
Product MonthDays Qty SumQty
------------------------------
A 2018-01-01 5 5
A 2018-02-01 3 8
A 2018-03-01 0 8
A 2018-04-01 0 8
A 2018-05-01 5 13
A 2018-06-01 0 13
A 2018-07-01 0 13
B 2018-08-01 10 23
B 2018-09-01 0 23
D 2018-10-01 12 35
D 2018-11-01 0 35
D 2018-12-01 0 35
First of all, i would divide month and year to get easier with statistics.
I will give you an example query, not based on your table but still helpful.
--here i create the table that will be used as calendar
Create Table MA_MonthYears (
Month int not null ,
year int not null
PRIMARY KEY ( month, year) )
--/////////////////
-- here i'm creating a procedure to fill the ma_monthyears table
declare #month as int
declare #year as int
set #month = 1
set #year = 2015
while ( #year != 2099 )
begin
insert into MA_MonthYears(Month, year)
select #month, #year
if #month < 12
set #month=#month+1
else
set #month=1
if #month = 1
set #year = #year + 1
end
--/////////////////
--here you are the possible result you are looking for
select SUM(Ma_saledocdetail.taxableamount) as Sold, MA_MonthYears.month , MA_MonthYears.year , item
from MA_MonthYears left outer join MA_SaleDocDetail on year(MA_SaleDocDetail.DocumentDate) = MA_MonthYears.year
and Month(ma_saledocdetail.documentdate) = MA_MonthYears.Month
group by MA_SaleDocDetail.Item, MA_MonthYears.year , MA_MonthYears.month
order by MA_MonthYears.year , MA_MonthYears.month

SQL - Find if column dates include at least partially a date range

I need to create a report and I am struggling with the SQL script.
The table I want to query is a company_status_history table which has entries like the following (the ones that I can't figure out)
Table company_status_history
Columns:
| id | company_id | status_id | effective_date |
Data:
| 1 | 10 | 1 | 2016-12-30 00:00:00.000 |
| 2 | 10 | 5 | 2017-02-04 00:00:00.000 |
| 3 | 11 | 5 | 2017-06-05 00:00:00.000 |
| 4 | 11 | 1 | 2018-04-30 00:00:00.000 |
I want to answer to the question "Get all companies that have been at least for some point in status 1 inside the time period 01/01/2017 - 31/12/2017"
Above are the cases that I don't know how to handle since I need to add some logic of type :
"If this row is status 1 and it's date is before the date range check the next row if it has a date inside the date range."
"If this row is status 1 and it's date is after the date range check the row before if it has a date inside the date range."
I think this can be handled as a gaps and islands problem. Consider the following input data: (same as sample data of OP plus two additional rows)
id company_id status_id effective_date
-------------------------------------------
1 10 1 2016-12-15
2 10 1 2016-12-30
3 10 5 2017-02-04
4 10 4 2017-02-08
5 11 5 2017-06-05
6 11 1 2018-04-30
You can use the following query:
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
ORDER BY company_id, effective_date
to get:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 0
2 10 1 2016-12-30 1
3 10 5 2017-02-04 2
4 10 4 2017-02-08 2
5 11 5 2017-06-05 0
6 11 1 2018-04-30 0
Now you can identify status = 1 islands using:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
)
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
Output:
id company_id status_id effective_date grp
-----------------------------------------------
1 10 1 2016-12-15 1
2 10 1 2016-12-30 1
3 10 5 2017-02-04 1
4 10 4 2017-02-08 2
5 11 5 2017-06-05 1
6 11 1 2018-04-30 2
Calculated field grp will help us identify those islands:
;WITH CTE AS
(
SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY
(
SELECT COUNT(*) AS cnt
FROM company_status_history AS c
WHERE c.status_id = 1
AND c.company_id = t.company_id
AND c.effective_date < t.effective_date
) AS x
), CTE2 AS
(
SELECT id, company_id, status_id, effective_date,
ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) -
cnt AS grp
FROM CTE
)
SELECT company_id,
MIN(effective_date) AS start_date,
CASE
WHEN COUNT(*) > 1 THEN DATEADD(DAY, -1, MAX(effective_date))
ELSE MIN(effective_date)
END AS end_date
FROM CTE2
GROUP BY company_id, grp
HAVING COUNT(CASE WHEN status_id = 1 THEN 1 END) > 0
Output:
company_id start_date end_date
-----------------------------------
10 2016-12-15 2017-02-03
11 2018-04-30 2018-04-30
All you want know is those records from above that overlap with the specified interval.
Demo here with somewhat more complicated use case.
Maybe this is what you are looking for? For these kind of questions, you need to join two instance of your table, in this case I am just joining with next record by Id, which probably is not totally correct. To do it better, you can create a new Id using a windowed function like row_number, ordering the table by your requirement criteria
If this row is status 1 and it's date is before the date range check
the next row if it has a date inside the date range
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
else NULL
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
Implementing second criteria:
"If this row is status 1 and it's date is after the date range check
the row before if it has a date inside the date range."
declare #range_st date = '2017-01-01'
declare #range_en date = '2017-12-31'
select
case
when csh1.status_id=1 and csh1.effective_date<#range_st
then
case
when csh2.effective_date between #range_st and #range_en then true
else false
end
when csh1.status_id=1 and csh1.effective_date>#range_en
then
case
when csh3.effective_date between #range_st and #range_en then true
else false
end
else null -- ¿?
end
from company_status_history csh1
left join company_status_history csh2
on csh1.id=csh2.id+1
left join company_status_history csh3
on csh1.id=csh3.id-1
I would suggest the use of a cte and the window functions ROW_NUMBER. With this you can find the desired records. An example:
DECLARE #t TABLE(
id INT
,company_id INT
,status_id INT
,effective_date DATETIME
)
INSERT INTO #t VALUES
(1, 10, 1, '2016-12-30 00:00:00.000')
,(2, 10, 5, '2017-02-04 00:00:00.000')
,(3, 11, 5, '2017-06-05 00:00:00.000')
,(4, 11, 1, '2018-04-30 00:00:00.000')
DECLARE #StartDate DATETIME = '2017-01-01';
DECLARE #EndDate DATETIME = '2017-12-31';
WITH cte AS(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) AS rn
FROM #t
),
cteLeadLag AS(
SELECT c.*, ISNULL(c2.effective_date, c.effective_date) LagEffective, ISNULL(c3.effective_date, c.effective_date)LeadEffective
FROM cte c
LEFT JOIN cte c2 ON c2.company_id = c.company_id AND c2.rn = c.rn-1
LEFT JOIN cte c3 ON c3.company_id = c.company_id AND c3.rn = c.rn+1
)
SELECT 'Included' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Following' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date > #EndDate
AND LagEffective BETWEEN #StartDate AND #EndDate
UNION ALL
SELECT 'Trailing' AS RangeStatus, *
FROM cteLeadLag
WHERE status_id = 1
AND effective_date < #EndDate
AND LeadEffective BETWEEN #StartDate AND #EndDate
I first select all records with their leading and lagging Dates and then I perform your checks on the inclusion in the desired timespan.
Try with this, self-explanatory. Responds to this part of your question:
I want to answer to the question "Get all companies that have been at
least for some point in status 1 inside the time period 01/01/2017 -
31/12/2017"
Case that you want to find those id's that have been in any moment in status 1 and have records in the period requested:
SELECT *
FROM company_status_history
WHERE id IN
( SELECT Id
FROM company_status_history
WHERE status_id=1 )
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'
Case that you want to find id's in status 1 and inside the period:
SELECT *
FROM company_status_history
WHERE status_id=1
AND effective_date BETWEEN '2017-01-01' AND '2017-12-31'

Work out how many days it took from one status to another : SQL

Please feast your eyes on this current structure of our DB.
Our DBA is currently away for the next two weeks, I have very limited SQL knowledge, I like to stay with the UI and middle tier.
What we are trying to figure out is how can we do the following, we need to write a query to calculate the average period (in days) all commissions have taken to transition from ‘Verified’ to ‘Paid’ for a single dealer, currently the status are
Created
Verified
Rejected
Awaiting Payment
Paid
Refunded
I think this query needs to aim directly at the Commission History Table?
I'm not sure how I would go about writing such query due to the fact my knowledge on SQL is limited...
Any help would be great.
Here's a method to achieve what you're after, although it might not be the most efficient. It seems to me that it's more of a one off query you are looking to run, rather than something that you're going to run on a frequent enough to impact database performance.
Test Table Setup:
CREATE TABLE Commission
(
CommissionId INT,
DealerId INT
)
CREATE TABLE CommissionHistory
(
CommissionId INT,
ActionDate DATETIME,
NewPaymentStatusId INT
)
Insert Dummy Data - 5 Commissions for 1 Dealer:
INSERT INTO dbo.Commission
( CommissionId ,
DealerId
)
VALUES ( 1 , 1 ),
( 2 , 1 ),
( 3 , 1 ),
( 4 , 1 ),
( 5 , 1 ),
INSERT INTO dbo.CommissionHistory
( CommissionId ,
ActionDate ,
NewPaymentStatusId
)
VALUES ( 1 , GETDATE() -25, 1 ),
( 1 , GETDATE() -21, 2 ),
( 1 , GETDATE() -18, 3 ),
( 1 , GETDATE() -16, 4 ),
( 1 , GETDATE() -5, 5 ),
( 2 , GETDATE() -10, 1 ),
( 2 , GETDATE() -9, 2 ),
( 2 , GETDATE() -8, 3 ),
( 2 , GETDATE() -7, 4 ),
( 2 , GETDATE() -6, 5 ),
( 3 , GETDATE() -10, 1 ),
( 3 , GETDATE() -8, 2 ),
( 3 , GETDATE() -6, 3 ),
( 3 , GETDATE() -4, 4 ),
( 3 , GETDATE() -2, 5 ),
( 3 , GETDATE() -25, 6 ),
( 4 , GETDATE() -10, 1 ),
( 4 , GETDATE() -7, 2 ),
( 4 , GETDATE() -6, 3 ),
( 4 , GETDATE() -4, 4 ),
( 4 , GETDATE() -1, 5 ),
( 5 , GETDATE() -1, 1 ),
( 5 , GETDATE() -1, 2 )
So with the dummy data, Commissions 1, 2 &, 4 are classified as valid records as they have status 2 and 5. 3 is excluded as it is refunded and 5 is excluded as it's not paid.
To generate the averages I wrote the below query:
-- set the required dealer id
DECLARE #DealerId INT = 1
-- return all CommissionId's in to a temp table that have statuses 2 and 5, but not 6
SELECT DISTINCT CommissionId
INTO #DealerCommissions
FROM dbo.CommissionHistory t1
WHERE CommissionId IN (SELECT CommissionId
FROM dbo.Commission
WHERE DealerId = #DealerId)
AND NOT EXISTS (SELECT CommissionId
FROM dbo.CommissionHistory t2
WHERE t2.NewPaymentStatusId = 6 AND t2.CommissionId = t1.CommissionId)
AND EXISTS (SELECT CommissionId
FROM dbo.CommissionHistory t2
WHERE t2.NewPaymentStatusId = 2 AND t2.CommissionId = t1.CommissionId)
AND EXISTS (SELECT CommissionId
FROM dbo.CommissionHistory t2
WHERE t2.NewPaymentStatusId = 5 AND t2.CommissionId = t1.CommissionId)
-- use the temp table to return average difference between the MIN & MAX date
;WITH cte AS (
SELECT CommissionId FROM #DealerCommissions
)
SELECT AVG(CAST(DaysToCompletion AS DECIMAL(10,8)))
FROM (
SELECT DATEDIFF(DAY, MIN(ch.ActionDate), MAX(ch.ActionDate)) DaysToCompletion
FROM cte
INNER JOIN dbo.CommissionHistory ch ON ch.CommissionId = cte.CommissionId
GROUP BY ch.CommissionId
) AS averageDays
-- remove temp table
DROP TABLE #DealerCommissions
For every commission in history table you could get the max verified date and min paid date, assuming paid date always later than verified date. Then you can join commission table to group by dealer id to get the average duration in days.
with comm as(
select
commissionid,
max(case NewPamentStatus when 'Verified' then ActionDate else null end) as verified_date,
min(case NewPamentStatus when 'Paid' then ActionDate else null end) as paid_date
--using max or min just incase that same status will be recorded more than one time.
from
CommissionHistory
group by
commistionid
)
select
c.DealerId,
avg(datediff(day,comm.verified_date,comm.paid_date))
from
comm
inner join
commission c
on c.commissionid = comm.commissionid
where
datediff(day,comm.verified_date,comm.paid_date)>0
-- to get rid off the commissions with paid date before the verified date or in same day
group by
c.DealerId

TSQL Calendar table, count 10 workings days from date

I have a calendar table which stores rows of dates and an indication of wether that date is a holiday or working day.
How can I select the date that is 5 working days into the future from the 2014-12-22 so the selected date will be 2014-12-31
Date_Id Date_Date Date_JDE Is_WorkingDay
20141222 2014-12-22 114356 1
20141223 2014-12-23 114357 1
20141224 2014-12-24 114358 1
20141225 2014-12-25 114359 0
20141226 2014-12-26 114360 0
20141227 2014-12-27 114361 0
20141228 2014-12-28 114362 0
20141229 2014-12-29 114363 1
20141230 2014-12-30 114364 1
20141231 2014-12-31 114365 1
You can use a CTE like this...
;WITH cteWorkingDays AS
(
SELECT Date_Date, ROW_NUMBER() OVER (ORDER BY Date_Date) as 'rowNum'
FROM TableName
WHERE Is_WorkingDay = 1
and Date_Date > '20141222' -- this will be a param I suppose
)
SELECT Date_Date
FROM cteWorkingDays
WHERE rowNum = 5 -- this can be changed to 10 (title value
This is hand typed, but it will be close enough.
EDIT: Based on comment.
Declare #DateToUse TYPE -- unsure if you're using a string or a date type.
SELECT #DateToUse = Date_Date
FROM cteWorkingDays
WHERE rowNum = 5
...;
WITH DatesCTE AS
(
SELECT Date_Id,
Date_Date,
Date_JDE,
Is_WorkingDay,
ROW_NUMBER() OVER(ORDER BY Date_Date) AS rn
FROM DatesTable
WHERE Is_WorkingDay = 1
AND Date_Date > '2014-12-22'
)
SELECT Date_Date
FROM DatesCTE
WHERE rn = 5
SQL Fiddle Demo
with Derived Tables
select * from
(
SELECT Date_Date, ROW_NUMBER() OVER (ORDER BY Date_Date) as 'RowNum'
FROM Table_calendar
WHERE Is_WorkingDay = 1
and CAST(Date_Date as DATE) > '2014-12-22'
)d
where d.RowNum=5
You can Try Like This:
with calender as
(select top 5 date_id,date_date,date_jde from calender
where date_date>='2014-12-22' and is_workingday='1)calender
select top 1 * from calender order by date_date desc