SQL query to find results with no gap in dates - sql-server

I have a table with ClaimNumber, NoteCreateDate, NoteType
I wanted to find the claims that has notes like 'Review sent' back to back with no gap in the NoteCreateDate
eg.
+-------------+----------------+--------------------+----------+
| ClaimNumber | NoteCreateDate | Notes | NoteType |
+-------------+----------------+--------------------+----------+
| 12121 | 12/01/2017 | ReviewSent | Subject |
| 12121 | 12/05/2017 | PackagesenttoABC | Details |
| 12121 | 12/07/2017 | ReviewSent | Subject |
| 10005 | 05/06/2018 | ReviewSent | Subject |
| 10005 | 05/07/2018 | ReviewSent | Subject |
| 10005 | 05/08/2018 | ReviewSent | Subject |
| 10005 | 05/12/2018 | Fieldinvestigation | SIU |
+-------------+----------------+--------------------+----------+
Expected
From this example I wanted only the claim number 10005 since it the claim that has the notes 'Review Sent' back to back with no gap in the date(consecutive Dates). For instance, I wanted to find the claims that has the note with phrase 'ReviewSent' created today and the following note should also be the same 'Review sent', no matter when it was created, May be the next day or even 10 days later .. Thanks in Advance
My current MS SQL query.
select cm.ClaimNum, a.NoteCreateDate, a.Notes, a.NoteType
from CMaster cm
left join Note a on cm.ClaimNum = a.PARENTREF
left join NoteType] b on b.ID = a.TYPECODE
where Body like '%Review Sent%'

So if you want to find the ClaimNumber(s) where the ReviewSent notes are sent back-2-back then this query will get them
CREATE TABLE T1
([ClaimNumber] int, [NoteCreateDate] datetime, [Notes] varchar(18), [NoteType] varchar(7))
;
INSERT INTO T1
([ClaimNumber], [NoteCreateDate], [Notes], [NoteType])
VALUES
(12121, '2017-12-01 00:00:00', 'ReviewSent', 'Subject'),
(12121, '2017-12-05 00:00:00', 'PackagesenttoABC', 'Details'),
(12121, '2017-12-07 00:00:00', 'ReviewSent', 'Subject'),
(10005, '2018-05-06 00:00:00', 'ReviewSent', 'Subject'),
(10005, '2018-05-07 00:00:00', 'ReviewSent', 'Subject'),
(10005, '2018-05-08 00:00:00', 'ReviewSent', 'Subject'),
(10005, '2018-05-12 00:00:00', 'Fieldinvestigation', 'SIU')
;
SELECT DISTINCT X.ClaimNumber
FROM(
SELECT
ClaimNumber
,NoteCreateDate
,Notes
,NoteType
,LD=LEAD( Notes )OVER(PARTITION BY ClaimNumber ORDER BY NoteCreateDate ASC)
FROM dbo.T1
) X WHERE LD= 'ReviewSent' AND X.Notes= 'ReviewSent'
DROP TABLE dbo.T1
Result
ClaimNumber
10005

Select * into #tmp
from
(
Select 12121 as ClaimNumber,'12/01/2017' as
NoteCreateDate,'ReviewSent' as Notes,'Subject' as NoteType
union
Select 12121 , '12/05/2017' , 'PackagesenttoABC' , 'Details'
union
Select 12121 , '12/07/2017' , 'ReviewSent' ,
'Subject'
union
Select 10005 , '05/06/2018' , 'ReviewSent' , 'Subject'
union
Select 10005 , '05/07/2018' , 'ReviewSent' ,
'Subject'
union
Select 10005 , '05/08/2018' , 'ReviewSent' , 'Subject'
union
Select 10005 , '05/12/2018' , 'Fieldinvestigation' , 'SIU'
) t
/*create another temp table to simplify the query*/
Select * , ROW_NUMBER() OVER (PARTITION BY ClaimNumber ORDER BY
ClaimNumber)
as ClaimCounter into #tmp2 from #tmp
/** finally get the claimnos that have consecutive NoteCreate Date **/
SELECT distinct t1.ClaimNumber FROM #tmp2 t1
inner join #tmp2 t2 on (t1.ClaimCounter = t2.ClaimCounter - 1 and
t1.ClaimNumber = t2.ClaimNumber)
where
DATEDIFF(DAY,t1.NoteCreateDate,t2.NoteCreateDate) = 1
and
t1.Notes ='ReviewSent' and t2.Notes ='ReviewSent'

Related

Join created table under condition

I am creating a code to join two different tables under a certain condition. The tables look like this
(TABLE 2)
date | deal_code | originator | servicer | random |
-----------------------------------------------------
2011 | 001 | commerzbank | SPV1 | 1 |
2012 | 001 | commerzbank | SPV1 | 12 |
2013 | 001 | commerzbank | SPV1 | 7 |
2013 | 005 | unicredit | SPV2 | 7 |
and another table
(TABLE 1)
date | deal_code | amount |
---------------------------
2011 | 001 | 100 |
2012 | 001 | 100 |
2013 | 001 | 100 |
2013 | 005 | 200 |
I would like to have this as the final result
date | deal_code | amount | originator | servicer | random |
--------------------------------------------------------------
2013 | 001 | 100 | commerzbank | SPV1 | 7 |
2013 | 005 | 200 | unicredit | SPV2 | 7 |
I created the following code
select q1.deal_code, q1.date
from table1 q1
where q1.date = (SELECT MAX(t4.date)
FROM table1 t4
WHERE t4.deal_code = q1.deal_code)
that gives me:
(TABLE 3)
date | deal_code | amount |
---------------------------
2013 | 001 | 100 |
2013 | 005 | 200 |
That is the latest observation for table 1, now I would like to have the originator and servicer information given the deal_code and date. Any suggestion? I hope to have been clear enough. Thanks.
This should do what you are looking for. Please be careful when naming columns. Date is a reserved word and is too ambiguous to be a good name for a column.
declare #Something table
(
SomeDate int
, deal_code char(3)
, originator varchar(20)
, servicer char(4)
, random int
)
insert #Something values
(2011, '001', 'commerzbank', 'SPV1', 1)
, (2012, '001', 'commerzbank', 'SPV1', 12)
, (2013, '001', 'commerzbank', 'SPV1', 7)
, (2013, '005', 'unicredit ', 'SPV2', 7)
declare #SomethingElse table
(
SomeDate int
, deal_code char(3)
, amount int
)
insert #SomethingElse values
(2011, '001', '100')
, (2012, '001', '100')
, (2013, '001', '100')
, (2013, '005', '200')
select x.SomeDate
, x.deal_code
, x.originator
, x.servicer
, x.random
, x.amount
from
(
select s.SomeDate
, s.deal_code
, s.originator
, s.servicer
, s.random
, se.amount
, RowNum = ROW_NUMBER()over(partition by s.deal_code order by s.SomeDate desc)
from #Something s
join #SomethingElse se on se.SomeDate = s.SomeDate and se.deal_code = s.deal_code
) x
where x.RowNum = 1
Looks like this would work:
DECLARE #MaxYear INT;
SELECT #MaxYear = MAX(date)
FROM table1 AS t1
INNER JOIN table2 AS t2
ON t1.deal_code = t2.deal_code;
SELECT t1.date,
t1.deal_code,
t1.amount,
t2.originator,
t2.servicer,
t2.random
FROM table1 AS t1
INNER JOIN table2 AS t2
ON t1.date = #MaxYear
AND t1.deal_code = t2.deal_code;
I agree with Sean Lange about the date column name. His method gets around the dependency on the correlated sub-query, but at the heart of things, you really just need to add an INNER JOIN to your existing query in order to get the amount column into your result set.
select
q2.date,
q2.deal_code,
q1.amount,
q2.originator,
q2.servicer,
q2.random
from
table1 q1
join
table2 q2
on q1.date = q2.date
and q1.deal_code = q2.deal_code
where q1.date = (SELECT MAX(t4.date)
FROM table1 t4
WHERE t4.deal_code = q1.deal_code)

SQL Server 2014 Merging Overlapping Date Ranges

I have a table with 200.000 rows in a SQL Server 2014 database looking like this:
CREATE TABLE DateRanges
(
Contract VARCHAR(8),
Sector VARCHAR(8),
StartDate DATE,
EndDate DATE
);
INSERT INTO DateRanges (Contract, Sector, StartDate, Enddate)
SELECT '111', '999', '01-01-2014', '03-31-2014'
union
SELECT '111', '999', '04-01-2014', '06-30-2014'
union
SELECT '111', '999', '07-01-2014', '09-30-2014'
union
SELECT '111', '999', '10-01-2014', '12-31-2014'
union
SELECT '111', '888', '08-01-2014', '08-31-2014'
union
SELECT '111', '777', '08-15-2014', '08-31-2014'
union
SELECT '222', '999', '01-01-2014', '03-31-2014'
union
SELECT '222', '999', '04-01-2014', '06-30-2014'
union
SELECT '222', '999', '07-01-2014', '09-30-2014'
union
SELECT '222', '999', '10-01-2014', '12-31-2014'
union
SELECT '222', '666', '11-01-2014', '11-30-2014'
UNION
SELECT '222', '555', '11-15-2014', '11-30-2014';
As you can see there can be multiple overlaps for each contract and what I would like to have is the result like this
Contract Sector StartDate EndDate
---------------------------------------------
111 999 01-01-2014 07-31-2014
111 888 08-01-2014 08-14-2014
111 777 08-15-2014 08-31-2014
111 999 09-01-2014 12-31-2014
222 999 01-01-2014 10-31-2014
222 666 11-01-2014 11-14-2014
222 555 11-15-2014 11-30-2014
222 999 12-01-2014 12-31-2014
I can not figure out how this can be done and the examples i have seen on this site quite do not fit my problem.
This answer makes use of a few different techniques. The first is a recursive-cte that creates a table with every relevant cal_date which then gets cross apply'd with unique Contract values to get every combination of both values. The second is window-functions such as lag and row_number to determine a variety of things detailed in the comments below. Lastly, and probably most importantly, gaps-and-islands to determine when one Contract/Sector combination ends and the next begins.
Answer:
--determine range of dates
declare #bgn_dt date = (select min(StartDate) from DateRanges)
, #end_dt date = (select max(EndDate) from DateRanges)
--use a recursive CTE to create a record for each day / Contract
; with dates as
(
select #bgn_dt as cal_date
union all
select dateadd(d, 1, a.cal_date) as cal_date
from dates as a
where a.cal_date < #end_dt
)
select d.cal_date
, c.Contract
into #contract_dates
from dates as d
cross apply (select distinct Contract from DateRanges) as c
option (maxrecursion 0)
--Final Select
select f.Contract
, f.Sector
, min(f.cal_date) as StartDate
, max(f.cal_date) as EndDate
from (
--Use the sum-over to obtain the Island Numbers
select dr.Contract
, dr.Sector
, dr.cal_date
, sum(dr.IslandBegin) over (partition by dr.Contract order by dr.cal_date asc) as IslandNbr
from (
--Determine if the record is the start of a new Island
select a.Contract
, a.Sector
, a.cal_date
, case when lag(a.Sector, 1, NULL) over (partition by a.Contract order by a.cal_date asc) = a.Sector then 0 else 1 end as IslandBegin
from (
--Determine which Contract/Date combinations are valid, and rank the Sectors that are in effect
select cd.cal_date
, dr.Contract
, dr.Sector
, dr.EndDate
, row_number() over (partition by dr.Contract, cd.cal_date order by dr.StartDate desc) as ConractSectorRnk
from #contract_dates as cd
left join DateRanges as dr on cd.Contract = dr.Contract
and cd.cal_date between dr.StartDate and dr.EndDate
) as a
where a.ConractSectorRnk = 1
and a.Contract is not null
) as dr
) as f
group by f.Contract
, f.Sector
, f.IslandNbr
order by f.Contract asc
, min(f.cal_date) asc
Output:
+----------+--------+------------+------------+
| Contract | Sector | StartDate | EndDate |
+----------+--------+------------+------------+
| 111 | 999 | 2014-01-01 | 2014-07-31 |
| 111 | 888 | 2014-08-01 | 2014-08-14 |
| 111 | 777 | 2014-08-15 | 2014-08-31 |
| 111 | 999 | 2014-09-01 | 2014-12-31 |
| 222 | 999 | 2014-01-01 | 2014-10-31 |
| 222 | 666 | 2014-11-01 | 2014-11-14 |
| 222 | 555 | 2014-11-15 | 2014-11-30 |
| 222 | 999 | 2014-12-01 | 2014-12-31 |
+----------+--------+------------+------------+

Change output according to LAG

I have the current issue:
I'm trying to get the amount of time each of our workers have worked in a day to calculate our company's productivity. We have the time each of our workers has entered and left the building.
The rule is, sometimes our workers leaves the building to smoke or get something from the news stand outside, so we don't take that into consideration and count as if the person never left the building.
We have a cafeteria inside our building so most people don't actually leave the building to have lunch/dinner, so we just remove 1 hour from their productivity calculation, but, if they leave for more then 45 minutes, we will consider that the worker left to lunch/dinner.
I need the end result to look like this:
+----------+----------------+----------------+---------+----------+
| PersonID | IN | OUT | MINUTES | EatOut |
+----------+----------------+----------------+---------+----------+
| 1 | 20170807 08:00 | 20170807 17:25 | 465 | 1 |
+----------+----------------+----------------+---------+----------+
| 2 | 20170807 08:00 | 20170807 17:00 | 540 | 0 |
+----------+----------------+----------------+---------+----------+
My query I have so far:
DECLARE #mytable TABLE(
PersonId INT,
Situation VARCHAR(3),
SituationDtm DATETIME
);
INSERT INTO #mytable VALUES
(1, 'IN', '20170807 08:00'),
(1, 'OUT', '20170807 12:30'),
(1, 'IN', '20170807 14:00'),
(1, 'OUT', '20170807 17:15'),
(2, 'IN', '20170807 08:00'),
(2, 'OUT', '20170807 09:15'),
(2, 'IN', '20170807 09:30'),
(2, 'OUT', '20170807 17:00');
WITH CTE AS (
SELECT
[PersonId],
Situation AS 'CUR.Situation',
SituationDtm AS 'CUR.SituationDtm',
LEAD(Situation) OVER(PARTITION BY PersonId ORDER BY SituationDtm) AS 'NEXT.Situation',
LEAD(SituationDtm) OVER(PARTITION BY PersonId ORDER BY SituationDtm) AS 'NEXT.SituationDtm'
FROM
#mytable
)
SELECT
[CUR.Situation],
[CUR.SituationDtm],
[NEXT.Situation],
[NEXT.SituationDtm],
DATEDIFF(MINUTE, [CUR.SituationDtm], [NEXT.SituationDtm]) AS 'MINUTES'
FROM
CTE
Thanks in advance
You can further query as below: Since you are looking your solution in SQL Server 2008 where you do not have lead/lag you can query as below:
;With Cte as (
Select *, RowN = Row_Number() over(Partition by PersonId order by SituationDtm) from #mytable
), Cte2 as (
Select c1.*, c2.Situation as NextSituation, c2.SituationDtm as NextSituationDtm from cte c1 left join cte c2 on c1.RowN+1 = c2.RowN
and c1.PersonId = c2.PersonId
)
Select PersonId,
Min(SituationDTM) as [In],
Max(Situationdtm) as [Out],
Sum(Case when Situation = 'OUT' and NextSituation = 'IN' and datediff(mi,SituationDtm, NextSituationDTM) > 60 then 1 else 0 end) EatOut,
Sum(Case when Situation = 'OUT' and NextSituation = 'IN' and datediff(mi,SituationDtm, NextSituationDTM) > 60 then 0 else datediff(mi,SituationDtm, NextSituationDTM) end) as [minutes]
from Cte2
group by PersonId
In later versions after >= 2012 you can query as below:
Select PersonId,
Min(SituationDTM) as [In],
Max(Situationdtm) as [Out],
Sum(Case when Situation = 'OUT' and NextSituation = 'IN' and datediff(mi,SituationDtm, NextSituationDTM) > 60 then 1 else 0 end) EatOut,
Sum(Case when Situation = 'OUT' and NextSituation = 'IN' and datediff(mi,SituationDtm, NextSituationDTM) > 60 then 0 else datediff(mi,SituationDtm, NextSituationDTM) end) as [minutes]
from (
Select *, NextSituationDTM = lead(situationdtm) over (partition by personid order by situationdtm),
NextSituation = lead(Situation) over (partition by personid order by situationdtm) from #mytable
) a
group by PersonId
Output as below:
+----------+-------------------------+-------------------------+--------+---------+
| PersonId | In | Out | EatOut | minutes |
+----------+-------------------------+-------------------------+--------+---------+
| 1 | 2017-08-07 08:00:00.000 | 2017-08-07 17:15:00.000 | 1 | 465 |
| 2 | 2017-08-07 08:00:00.000 | 2017-08-07 17:00:00.000 | 0 | 540 |
+----------+-------------------------+-------------------------+--------+---------+

SQL Query construct

I have three tables. I want to get data from all those tables and put it in a virtual table. i am using SQL Server 2012.
Sorry if my format or tags are wrong because I m getting error Stack overflow requires external javascrip from another source domain, which is blocked of failed to load.
Booking Table
BookingId | date
======================
2 | 7/1/2017 (MM/dd/yyyy)
3 | 7/1/2017
BookingCost Table
Id | bookinId | Cost
==========================
1 | 2 | 2000
2 | 3 | 4000
Expense Table
Id | ExpenseCost | Date
======================
1 | 1400 | 7/2/2017 (MM/dd/yyyy)
2 | 1422 | 7/1/2017
3 | 4000 | 6/3/2017
I want to get Monthly result like following Table.
Date | Expense | Bookings
===================================
jan/2017 | 0 | 0
feb/2017 | 0 | 0
. | . | .
. | . | .
. | . | .
jun/2017 | 4000 | 0
jul/2017 | 2822 | 6000
. | . | .
. | . | .
. | . | .
How is something like this (assuming your dates are DATE types and not VARCHAR - otherwise you could convert them).
SELECT COALESCE(EXPENSE.MONTH, BOOKINGS.MONTH) [Date], EXPENSE.Cost Expense, BOOKINGS.Cost Bookings
FROM (
SELECT DATEADD(DD,1-DAY([date]),[date]) MONTH, SUM(Cost) Cost
FROM Booking
INNER JOIN BookingCost
ON Booking.BookingID = BookingCost.BookingID
GROUP BY DATEADD(DD,1-DAY([date]),[date])
) BOOKINGS
FULL JOIN (
SELECT DATEADD(DD,1-DAY([date]),[date]) MONTH, SUM(ExpenseCost) Cost
FROM Expense
GROUP BY DATEADD(DD,1-DAY([date]),[date])
) EXPENSE
ON EXPENSE.MONTH = BOOKINGS.MONTH
ORDER BY 1
To also get the 0 counts, you could left join the totals to a tally table which has all the months for the year.
The sql is using FORMAT to transform the Date
For example:
;WITH MONTHS AS
(
select
[Year], [Month],
format(datefromparts([Year],[Month],1),'MMM/yyyy') as [MonthYear]
from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) m([Month])
cross join (values (2017)) y([Year])
)
select
m.[MonthYear] as [Date],
coalesce(e.TotalExpense,0) as Expense,
coalesce(bc.TotalCost,0) as Bookings
from MONTHS m
left join (
select
datepart(year,[Date]) as [Year],
datepart(month,[Date]) as [Month],
sum(ExpenseCost) as TotalExpense
from Expense
where datepart(year,[Date]) in (select distinct [Year] from MONTHS)
group by datepart(year,[Date]), datepart(month,[Date])
)e on (e.[Year] = m.[Year] and e.[Month] = m.[Month])
left join (
select
datepart(year,b.[date]) as [Year],
datepart(month,b.[date]) as [Month],
sum(c.Cost) as TotalCost
from Booking b
join BookingCost c on c.BookingId = b.BookingId
where datepart(year,b.[date]) in (select distinct [Year] from MONTHS)
group by datepart(year,b.[date]), datepart(month,b.[date])
) bc
on (bc.[Year] = m.[Year] and bc.[Month] = m.[Month])
order by m.[Year], m.[Month];
Test data I used
declare #Booking table (BookingId int, [date] date);
insert into #Booking (BookingId,[date]) values (2,'2017-07-01'),(3,'2017-07-01');
declare #BookingCost table (Id int, BookingId int, Cost int);
insert into #BookingCost (Id, BookingId, Cost) values (1,2,2000),(2,3,4000);
declare #Expense table (Id int, ExpenseCost int, [Date] date);
insert into #Expense (Id, ExpenseCost, [Date]) values
(1,1400,'2017-07-02'),(2,1422,'2017-07-01'),(3,4000,'2017-06-03');

How Can I Order in an SQL Statement?

I have these tables: Stock, Unit, Location, Category, StockBalance
At StockBalance: there is StockID from Stock, UnitId from Unit, LocationID from Location
I save at StockBalance Table like following
StockBalanceID | StockID | UnitID | LocationID | BalanceQuantity
1 | 1 | 1 | 1 | 20
2 | 1 | 2 | 1 | 30
3 | 1 | 3 | 1 | 40
4 | 2 | 1 | 2 | 20
5 | 2 | 2 | 2 | 30
6 | 2 | 3 | 2 | 40
I would like to show on Classic ASP as :
Group By : CategoryName
Stock Name Quantity Location Name
Qty | Unit | Qty | Unit | Qty | Unit
Stock One | 20 | One | 30 | Two | 40 | Three | Location One
Stock Two | 20 | One | 30 | Two | 40 | Three | Location Two
How can I Select From StockBalance to get like above? How about my edit one?
Please help me !
PIVOT is what you want.
First some sample data setup:
create table yourTable (
StockBalanceID int, StockID int, UnitID int, LocationID int, BalanceQuantity int);
insert yourTable
select 1 , 1 , 1 , 1 , 20
union all select 2 , 1 , 2 , 1 , 30
union all select 3 , 1 , 3 , 1 , 40
union all select 4 , 2 , 1 , 2 , 20
union all select 5 , 2 , 2 , 2 , 30
union all select 6 , 2 , 3 , 2 , 40
;
Now to do the work...
select StockID, LocationID,
sum(case UnitID when 1 then BalanceQuantity end) as [Unit One],
sum(case UnitID when 2 then BalanceQuantity end) as [Unit Two],
sum(case UnitID when 3 then BalanceQuantity end) as [Unit Three]
from yourTable
group by StockID, LocationID;
Try the following
DECLARE #StockBalances TABLE(
StockBalanceID INT,
StockID INT,
UnitID INT,
LocationID INT,
BalanceQuantity FLOAT
)
DECLARE #Stock TABLE(
StockID INT,
StockName VARCHAR(10)
)
DECLARE #Unit TABLE(
UnitID INT,
UnitName VARCHAR(10)
)
DECLARE #Location TABLE(
LocationID INT,
LocationName VARCHAR(10)
)
INSERT INTO #StockBalances SELECT 1,1,1,1,20
INSERT INTO #StockBalances SELECT 2,1,2,1,30
INSERT INTO #StockBalances SELECT 3,1,3,1,40
INSERT INTO #StockBalances SELECT 4,2,1,2,20
INSERT INTO #StockBalances SELECT 5,2,2,2 ,30
INSERT INTO #StockBalances SELECT 6,2,3,2,40
INSERT INTO #Stock SELECT 1, 'Stock 1'
INSERT INTO #Stock SELECT 2, 'Stock 2'
INSERT INTO #Unit SELECT 1, 'Unit 1'
INSERT INTO #Unit SELECT 2, 'Unit 2'
INSERT INTO #Unit SELECT 3, 'Unit 3'
INSERT INTO #Location SELECT 1, 'Location 1'
INSERT INTO #Location SELECT 2, 'Location 2'
SELECT *
FROM (
SELECT s.StockName,
sb.BalanceQuantity,
u.UnitName,
l.LocationName
FROM #StockBalances sb INNER JOIN
#Stock s ON sb.StockID = s.StockID INNER JOIN
#Unit u ON sb.UnitID = u.UnitID INNER JOIN
#Location l ON sb.LocationID = l.LocationID
) t
PIVOT (SUM(BalanceQuantity) FOR UnitName IN ([Unit 1], [Unit 2], [Unit 3])) p

Resources