SQL Server : list by Nearest Birthday - sql

Table A - column B [DateTime]
Need list users in that table, ordering at nearest birthday. Users who have already made birthday should be at the end of the list considering them for the following year.
select B from A (order by/where)?
Table Example
USER DATE
MARCELO 1988-04-11
RICARDO 1965-12-30
WILSON 1977-02-20
PABLO 1985-01-10
JOHN NULL
Expected Result
WILSON 20/02 (Month/Day)
MARCELO 11/04
RICARDO 30/12
PABLO 10/01
(JOHN NOT IN THE LIST)

Test Data
DECLARE #TABLE TABLE(Name VARCHAR(100),Dob DATETIME)
INSERT INTO #TABLE VALUES
('Mark', '19961017'),('Josh', '19801119'),('Sam', '19700709'),
('Vicky', '19500210'),('Dom', '19890308'),('Paul', '19840401')
,('Nick', NULL)
Query
SELECT Name, CAST(MONTH(Dob) AS NVARCHAR(2))
+ '/' + CAST(DAY(Dob) AS NVARCHAR(2)) [Dob Month/Day]
FROM #TABLE
WHERE DATEPART(DAYOFYEAR,Dob) - DATEPART(DAYOFYEAR,GETDATE()) > 0
ORDER BY ABS(DATEPART(DAYOFYEAR,Dob) - DATEPART(DAYOFYEAR,GETDATE()))
Result Set
User vicky will be filtered out as birthday is gone.
╔══════╦═══════════════╗
║ Name ║ Dob Month/Day ║
╠══════╬═══════════════╣
║ Dom ║ 3/8 ║
║ Paul ║ 4/1 ║
║ Sam ║ 7/9 ║
║ Mark ║ 10/17 ║
║ Josh ║ 11/19 ║
╚══════╩═══════════════╝
Result set is ordered by who's birthday is next

select name, birthdate
from yourtable
where birthdate is not null
order by datepart(dy,dateadd(d,- DATEPART(dy, getdate()),birthdate ))

This solution:
"wraps around" for a full year.
and also allows a "how many months to project out" filter.
IF OBJECT_ID('tempdb..#Employee') IS NOT NULL
begin
drop table #Employee
end
CREATE TABLE #Employee
(
SurrogateKeyIDENTITY int not null IDENTITY (1,1) ,
NameOf varchar(12) not null ,
BirthDate datetime not null
)
Insert into #Employee (NameOf, BirthDate)
Select 'A', '01/01/1999'
UNION ALL Select 'B', '01/16/1941'
UNION ALL Select 'C', '01/29/1965'
UNION ALL Select 'D', '02/13/1944'
UNION ALL Select 'P', '02/14/1978'
UNION ALL Select 'Q', '02/15/1984'
UNION ALL Select 'R', '03/13/1948'
UNION ALL Select 'S', '04/16/1983'
UNION ALL Select 'T', '05/17/1953'
UNION ALL Select 'U', '07/19/1959'
UNION ALL Select 'V', '08/16/1959'
UNION ALL Select 'W', '09/1/1959'
UNION ALL Select 'X', '10/30/1959'
UNION ALL Select 'Y', '11/16/1959'
UNION ALL Select 'Z', '12/31/1972'
Declare #MyCURRENT_TIMESTAMP datetime
/* select #MyCURRENT_TIMESTAMP = CURRENT_TIMESTAMP */
select #MyCURRENT_TIMESTAMP = '02/14/2014'
Declare #NumberOfMonthsToGoOut int
select #NumberOfMonthsToGoOut = 12
;WITH myCalculationsCTE (NameOf, BirthDate, [NoYearBirthDate] , [NoYearCurrentDate] )
AS
(
SELECT NameOf, BirthDate
, DATEFROMPARTS( 1900 , MONTH(BirthDate), DAY (BirthDate)) [NoYearBirthDate]
, DATEFROMPARTS( 1900 , MONTH(#MyCURRENT_TIMESTAMP), DAY (#MyCURRENT_TIMESTAMP)) [NoYearCurrentDate]
FROM #Employee
WHERE BirthDate IS NOT NULL
)
,
myCTE (NameOf, BirthDate, [NoYearBirthDate] , [NoYearCurrentDate] , DerivedMonthDiff)
AS
(
SELECT NameOf, BirthDate
, [NoYearBirthDate]
, [NoYearCurrentDate]
, [DerivedMonthDiff] = CASE
WHEN [NoYearBirthDate] >= [NoYearCurrentDate]
then DATEDIFF(m , [NoYearCurrentDate], [NoYearBirthDate] )
else
DATEDIFF(m , [NoYearCurrentDate], [NoYearBirthDate]) + 12 end
FROM myCalculationsCTE
)
SELECT NameOf, BirthDate , [NoYearBirthDate] , [NoYearCurrentDate]
, DerivedMonthDiff
, MONTH([NoYearBirthDate]) [Month]
, DAY ([NoYearBirthDate]) [DayOf]
FROM myCTE
where [DerivedMonthDiff] <= #NumberOfMonthsToGoOut
order by DerivedMonthDiff , MONTH([NoYearBirthDate]) , DAY ([NoYearBirthDate])
IF OBJECT_ID('tempdb..#Employee') IS NOT NULL
begin
drop table #Employee
end

SELECT Name, CAST(MONTH(BIRTHDAY) AS NVARCHAR(2))
+ '/' + CAST(DAY(BIRTHDAY) AS NVARCHAR(2)) [Dob Month/Day],DATEPART(DAYOFYEAR,BIRTHDAY) as [dayofyear],
case when (DATEPART(DAYOFYEAR,BIRTHDAY) - DATEPART(DAYOFYEAR,GETDATE()) > 0)
THEN
DATEPART(DAYOFYEAR,BIRTHDAY) - DATEPART(DAYOFYEAR,GETDATE())
ELSE
DATEPART(DAYOFYEAR,BIRTHDAY) + 365-DATEPART(DAYOFYEAR,GETDATE())
END
AS [DAYSTILLBURTHDAY]
FROM CONTACTS
WHERE BIRTHDAY <> '9999-01-01 00:00:00.000'
ORDER BY (case when (DATEPART(DAYOFYEAR,BIRTHDAY) - DATEPART(DAYOFYEAR,GETDATE()) > 0)
THEN
DATEPART(DAYOFYEAR,BIRTHDAY) - DATEPART(DAYOFYEAR,GETDATE())
ELSE
DATEPART(DAYOFYEAR,BIRTHDAY) + 365-DATEPART(DAYOFYEAR,GETDATE())
END)

Related

distribute accumulated working hours through days

I have Date time when engine has started working and how long was it working. but sometimes it can work more than 24 Hours.
if it worked for 28 Hours on the starting date i will have record
Name started_working Finished working hours_worked
obj-00123 07/02/2018 13:30 08/02/2018 17:30 28
I need to to have record that will show that engine has worked for 10:30 in 07 and 17:30 in 08.
Name started_working Finished working hours_worked
obj-00123 07/02/2018 13:30 07/02/2018 00:00 10:30
obj-00123 07/02/2018 13:30 08/02/2018 17:30 17:30
or something like that. I don't have any idea how can i get this done. can you give me some clues. i dont ask for writing code if its not too easy.
thank you
This might do the trick for you
--Using CTE to show sample data
;WITH cteX( Name,started_working,Finished_working)
AS
(
SELECT
'obj-00123','07/02/2018 13:30','08/02/2018 17:30' UNION ALL
SELECT 'obj-00155','07/02/2018 15:00','07/02/2018 22:30'
)
SELECT
X.Name
, X.started_working
, X.Finished_working
, HoursWorked = CONVERT(VARCHAR(12), DATEADD(minute, DATEDIFF(minute, X.started_working, X.Finished_working), 0), 114)
FROM
(
SELECT
T1.Name
,T1.started_working
,Finished_working = DATEADD(SECOND,0,DATEADD(DAY, DATEDIFF(DAY,-1,T1.started_working),0)) -- Dummy finish time # Midnight
FROM
cteX T1
WHERE
DATEDIFF(DAY,T1.started_working,T1.Finished_working) <> 0 --Create a dummy finish time #Midnight when start and end not on same day
UNION ALL
SELECT
T2.Name
,started_working = CASE WHEN DATEDIFF(DAY,T2.started_working,T2.Finished_working) <> 0
THEN DATEADD(DAY, DATEDIFF(DAY, 0, T2.Finished_working), 0) --Start # Midnight
ELSE T2.started_working
END
,T2.Finished_working
FROM
cteX T2
) X
ORDER BY
X.Name, X.started_working
OUTPUT
Name started_working Finished_working HoursWorked
obj-00123 2018-07-02 13:30:00.000 2018-07-03 00:00:00.000 10:30:00:000
obj-00123 2018-08-02 00:00:00.000 2018-08-02 17:30:00.000 17:30:00:000
obj-00155 2018-07-02 15:00:00.000 2018-07-02 22:30:00.000 07:30:00:000
According to your sample data working hours may be more than several days. In this case you need to use tally table or recursive CTE. I have used recursive CTE since it's easier to handle result fields. Also there are two columns in result named started_working and started_working2. started_working is from your expected output, but I believe you need started_working2 column
declare #T as table (
Name varchar(100)
, started_working datetime
, finished_working datetime
--, hours_worked int
)
insert into #T
values
('obj-00123', '20180207 13:30', '20180208 17:30')
, ('obj-00123', '20180208 19:00', '20180209 05:00')
, ('obj-00123', '20180209 19:00', '20180209 22:00')
, ('obj-00123', '20180210 19:00', '20180213 22:00')
;with rcte as (
select
*, started_working2 = started_working
, next_date = cast(dateadd(dd, 1, started_working) as date), 1 step
from
#T
union all
select
Name, started_working, finished_working
, cast(next_date as datetime)
, dateadd(dd, 1, next_date), step + 1
from
rcte
where
next_date < finished_working
)
select
Name, started_working, started_working2, finished_working
, right(replace(str(diff / 60), ' ', 0), 2) + ':' + right(replace(str(diff % 60), ' ', 0), 2) hours_worked
from (
select
Name, started_working
, case
when step = 1 then started_working
else started_working2
end started_working2
, case
when step = max(step) over (partition by Name, started_working)
then finished_working else next_date
end finished_working
from
rcte
) t
cross apply (select datediff(mi, started_working2, finished_working) diff) ca
I'd approach the solution something like this:
WITH dynamic_twelths_of_hr_table(datetime2_value) AS
(
SELECT '2017-01-01'
UNION ALL
SELECT DATEADD(MINUTE, 5, datetime2_value)
FROM dynamic_twelths_of_hr_table
WHERE DATEADD(MINUTE, 5, datetime2_value) <= '2019-01-01'
)
,twelths_hr_table AS
(
SELECT
DATEADD(DAY, DATEDIFF(DAY, 0, datetime2_value), 0) AS date_value
,datetime2_value
FROM dynamic_twelths_of_hr_table
)
,modified_source_table AS
(
SELECT
name
,objectid
,engine_start
,ISNULL(engine_stop, GETDATE()) AS engine_stop
,IIF(engine_start IS NULL OR engine_stop IS NULL, 1, 0) AS is_still_running
FROM [YOUR_SOURCE_TABLE]
)
SELECT
name
,objectid
,is_still_running
,date_value
,(COUNT(datetime2_value)/12.0) AS hours_run_on_this_day
FROM
modified_source_table
LEFT JOIN
twelths_hr_table AS tht
ON (tht.datetime2_value BETWEEN engine_start AND engine_stop)
GROUP BY
name, objectid, is_still_running, date_value
ORDER BY
name, objectid, is_still_running, date_value
Note I haven't tested this code so please excuse any small syntax errors.
I've also baked in an assumption about the range of dates to be considered (these can be widened, or made dynamic based on when the query runs), and it has a 5 minute resolution (based on the fact that, at a glance, I could only see one value in the engine_stop column that didn't fall on a 5-minute threshold - so I assume sub-5-minute precision is not required).
Basically what it does is expand each engine row out into 5-minute windows (twelths of an hour), and then simply groups these by day and counts the number of windows per day during which the engine was running.
For currently-running engines, it will calculate how long it has run so far. I trust you can tweak the code to your exact requirements.
thank you to all. this worked perfectly. it needed slight polishing and recursion needed to be set to 0.
But creating view is a trouble with CTE.
create view mroobjectenginerowkinghoursdeclare as
declare #T as table (
Name nvarchar(100)
, OBJECTID varchar(50)
, started_working datetime
,STOPFROM datetime
,STARTDATE datetime
,STOPDATE datetime
,MODIFIEDDATETIME datetime
,START_STOP int
,STARTDESCRIPTION nvarchar(300)
,STOPDESCRIPTION nvarchar(300)
,wattage nvarchar (50)
,purpose nvarchar(300)
,location nvarchar(300)
,finished_working datetime
,oldDiff int
)
insert into #T
select
NAME
,OBJECTID
,STOPTO
,STOPFROM
,STARTDATE
,STOPDATE
,MODIFIEDDATETIME
,START_STOP
,STARTDESCRIPTION
,STOPDESCRIPTION
,wattage
,purpose
,location
,next_stopfrom
,diff
FROM [MicrosoftDynamicsAX].[dbo].[mroobjectengineworkinghours]
;with rcte as (
select
*, started_working2 = started_working
, next_date = cast(dateadd(dd, 1, started_working) as date), 1 step
from
#T
union all
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location, finished_working,oldDiff
, cast(next_date as datetime)
, dateadd(dd, 1, next_date), step + 1
from
rcte
where
next_date < finished_working
)
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location,oldDiff, started_working2, finished_working
, right(replace(str(diff / 60), ' ', 0), 2) + ':' + right(replace(str(diff % 60), ' ', 0), 2) hours_worked
from (
select
Name,OBJECTID, started_working,STOPFROM,STARTDATE,STOPDATE,MODIFIEDDATETIME,START_STOP,STARTDESCRIPTION
,STOPDESCRIPTION,wattage
,purpose
,location,oldDiff
, case
when step = 1 then started_working
else started_working2
end started_working2
, case
when step = max(step) over (partition by Name, started_working)
then finished_working else next_date
end finished_working
from
rcte
) t
cross apply (select datediff(mi, started_working2, finished_working) diff) ca
OPTION (MAXRECURSION 0);

T-SQL: calculate age then add character to result

Been stuck on this one for a while now. Let's say I have a Client table like the one here:
Name BirthDayNum BirthMonthNum BirthYearNum
--------------------------------------------------
John 23 12 1965
Jane 4 9 1975
Joe 6 3 1953
Currently I am calculating the age using this syntax: (sorry if it is hard to read)
DATEDIFF(year, CONVERT(datetime, CAST(client.BirthMonthNum AS varchar(2))
+ '-' + CAST(client.BirthDayNum AS varchar(2))
+ '-' + CAST(client.BirthYearNum AS varchar(4)), 101), GETDATE())
- (CASE WHEN dateadd(YY, DATEDIFF(year, CONVERT(datetime, CAST(client.BirthMonthNum AS varchar(2))
+ '-' + CAST(client.BirthDayNum AS varchar(2))
+ '-' + CAST(client.BirthYearNum AS varchar(4)), 101), GETDATE()),
CONVERT(datetime, CAST(client.BirthMonthNum AS varchar(2))
+ '-' + CAST(client.BirthDayNum AS varchar(2))
+ '-' + CAST(client.BirthYearNum AS varchar(4)), 101)) > getdate() THEN 1 ELSE 0 END) AS 'Client Age'
This will give me the age in years. Of course if I want months, I just change the DATEDIFF(year to month. So, what I am trying to do now is this.
Continue to calculate the age, but instead of returning either years or months, I would like to return the age in years and months, but also, concat a 'y' and 'm' within the value as well. Ex. 41y 11m for Jane above.
So basically I am trying to figure out how to add a char to the return value, as well as calculate the remaining months beyond the year calculation.
Any help would be greatly appreciated!
Tired of twisting myself into knots with date calculations, I created a Table-Valued-Function to calculate elapsed time in Years, Months, Days, Hours, Minutes, and Seconds.
Example
Declare #YourTable table (Name varchar(50),BirthDayNum int, BirthMonthNum int, BirthYearNum int)
Insert Into #YourTable values
('John', 23, 12, 1965),
('Jane', 4, 9, 1975),
('Joe', 6, 3, 1953)
Select A.Name
,B.*
,Age = concat(C.Years,'y ',C.Months,'m')
From #YourTable A
Cross Apply (Select DOB = DateFromParts(A.BirthYearNum,A.BirthMonthNum,A.BirthDayNum)) B
Cross Apply [dbo].[udf-Date-Elapsed](B.DOB,GetDate()) C
Returns
Name DOB Age
John 1965-12-23 51y 3m
Jane 1975-09-04 41y 6m
Joe 1953-03-06 64y 0m
The UDF - May look like overkill, but it is very performant
CREATE FUNCTION [dbo].[udf-Date-Elapsed] (#D1 DateTime,#D2 DateTime)
Returns Table
Return (
with cteBN(N) as (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cteRN(R) as (Select Row_Number() Over (Order By (Select NULL))-1 From cteBN a,cteBN b,cteBN c),
cteYY(N,D) as (Select Max(R),Max(DateAdd(YY,R,#D1))From cteRN R Where DateAdd(YY,R,#D1)<=#D2),
cteMM(N,D) as (Select Max(R),Max(DateAdd(MM,R,D)) From (Select Top 12 R From cteRN Order By 1) R, cteYY P Where DateAdd(MM,R,D)<=#D2),
cteDD(N,D) as (Select Max(R),Max(DateAdd(DD,R,D)) From (Select Top 31 R From cteRN Order By 1) R, cteMM P Where DateAdd(DD,R,D)<=#D2),
cteHH(N,D) as (Select Max(R),Max(DateAdd(HH,R,D)) From (Select Top 24 R From cteRN Order By 1) R, cteDD P Where DateAdd(HH,R,D)<=#D2),
cteMI(N,D) as (Select Max(R),Max(DateAdd(MI,R,D)) From (Select Top 60 R From cteRN Order By 1) R, cteHH P Where DateAdd(MI,R,D)<=#D2),
cteSS(N,D) as (Select Max(R),Max(DateAdd(SS,R,D)) From (Select Top 60 R From cteRN Order By 1) R, cteMI P Where DateAdd(SS,R,D)<=#D2)
Select [Years] = cteYY.N
,[Months] = cteMM.N
,[Days] = cteDD.N
,[Hours] = cteHH.N
,[Minutes] = cteMI.N
,[Seconds] = cteSS.N
From cteYY,cteMM,cteDD,cteHH,cteMI,cteSS
)
--Max 1000 years
--Select * from [dbo].[udf-Date-Elapsed] ('1991-09-12 21:00:00.000',GetDate())
Just to Illustrate
The TVF without any secondary string manipulation would return
Select A.Name
,B.*
From #YourTable A
Cross Apply [dbo].[udf-Date-Elapsed](DateFromParts(A.BirthYearNum,A.BirthMonthNum,A.BirthDayNum),GetDate()) B
EDIT - READ ONLY VERSION
Select A.Name
,B.*
,Age = concat(DateDiff(MONTH,B.DOB,GetDate())/12,'y ',DateDiff(MONTH,B.DOB,GetDate()) % 12,'m')
From #YourTable A
Cross Apply (Select DOB = DateFromParts(A.BirthYearNum,A.BirthMonthNum,A.BirthDayNum)) B
Yes it is easier to save as DOB.. But one simple method
select concat( floor(datediff(year, datefromparts(birthyearnum,birthmonthnum,birthdaynum), getdate()))-1, 'y ', datediff(month, datefromparts(birthyearnum,birthmonthnum,birthdaynum), getdate())%12, 'm')
from #yourDates
How age for 1965 is 41y?
Input table:
create table #yourdates(Name varchar(10), BirthdayNum int, BirthMonthNum int, BirthYearNum int)
insert into #yourdates
(Name, BirthdayNum, BirthMonthNum, BirthYearNum) values
('John', 23 , 12 , 1965 )
,('Jane', 4 , 9 , 1975 )
,('Joe ', 6 , 3 , 1953 )
If you are on 2008 or lesser and can't use datefromparts...
declare #table table ([Name] varchar(4), BirthDayNum int, BirthMonthNum int, BirthYearNum int)
insert into #table
values
('John',23,12,1965),
('Jane',4,9,1975),
('Day',30,3,1990)
;with cte as(
select
[Name],
cast(BirthYearNum as varchar(4)) + '/' + cast(BirthMonthNum as varchar(2)) + '/' + cast(BirthDayNum as varchar(2)) as DOB
from
#table)
select
[Name]
,DOB
,datediff(year,DOB,GETDATE()) as Years
,datediff(month,DOB,GETDATE()) %12 as Months
,rtrim(cast(datediff(year,DOB,GETDATE()) as char(2))) + 'y ' + rtrim(cast(datediff(month,DOB,GETDATE()) %12 as char(2))) + 'm' as Age
from cte

How to show monthly data even if there are no results yet SQL Server 2008

So I wrote a script that would show monthly premium. Say if you want to view the total premium up to November, you can pass through a parameter in in SSRS to pick 1/1/2016 - 11/30/2016. This would only show the data up until november, hoever, I would like to show it up until december even if there are no records there. How do I go about doing this in SQL? Here is my script so far:
SELECT lc.[Date]
,lc.Carrier
,lc.[Direct Ceded Written Premium]
,cast(cast(year(lc.[date]) as varchar(4)) + '-' + cast(month(lc.[date]) as varchar(2)) + '-01' as date) as [begofmonth]
from
(
SELECT
CASE
WHEN pd.TransactionEffDate < pd.TransactionDate THEN cast(pd.TransactionDate as DATE)
WHEN pd.TransactionEffDate < pd.EffectiveDate THEN cast(pd.EffectiveDate as DATE)
ELSE cast(pd.TransactionEffDate as date)
END AS [Date]
,CASE WHEN LEFT(PD.POLICYNUM, 3) = 'ORV'
THEN 'Palomar Value Select OR'
WHEN LEFT(PD.POLICYNUM, 3) = 'VSE'
THEN 'Palomar Value Select CA'
WHEN LEFT(PD.POLICYNUM, 3) = 'WAV'
THEN 'Palomar Value Select WA'
ELSE 'Palomar' END AS [Carrier]
,ISNULL(SUM(pd.WrittenPremium), 0) AS [Direct Ceded Written Premium]
FROM premdetail pd
JOIN transactionpremium tp ON pd.systemid = tp.systemid
AND pd.transactionpremiumid = tp.id
JOIN transactionhistory th ON tp.systemid = th.systemid
AND tp.cmmcontainer = th.cmmcontainer
AND tp.parentid = th.id
JOIN basicpolicy bp ON th.systemid = bp.systemid
AND th.cmmcontainer = bp.cmmcontainer
AND th.parentid = bp.id
WHERE
(CASE
WHEN pd.TransactionEffDate < pd.TransactionDate THEN pd.TransactionDate
WHEN pd.TransactionEffDate < pd.EffectiveDate THEN pd.EffectiveDate
ELSE pd.TransactionEffDate
END) > = CAST(#StartDate AS DATE)
AND (CASE
WHEN pd.TransactionEffDate < pd.TransactionDate THEN pd.TransactionDate
WHEN pd.TransactionEffDate < pd.EffectiveDate THEN pd.EffectiveDate
ELSE pd.TransactionEffDate
END) < CAST(#EndDate + 1 AS DATE)
AND (bp.carriercd = #ResEQCarrierCd
OR #ResEQCarrierCd = 'All')
GROUP BY
CASE
WHEN pd.TransactionEffDate < pd.TransactionDate THEN cast(pd.TransactionDate as DATE)
WHEN pd.TransactionEffDate < pd.EffectiveDate THEN cast(pd.EffectiveDate as DATE)
ELSE cast(pd.TransactionEffDate as date)
END
,CONVERT(VARCHAR, pd.EffectiveDate, 101)
,CONVERT(VARCHAR, pd.ExpirationDate, 101)
,CASE
WHEN LEFT(PD.POLICYNUM, 3) = 'ORV'
THEN 'Palomar Value Select OR'
WHEN LEFT(PD.POLICYNUM, 3) = 'VSE'
THEN 'Palomar Value Select CA'
WHEN LEFT(PD.POLICYNUM, 3) = 'WAV'
THEN 'Palomar Value Select WA'
ELSE 'Palomar'
END
,CASE
WHEN pd.TransactionCode = 'EN' THEN CONVERT(VARCHAR, th.TransactionEffectiveDt, 101)
ELSE ''
END
,CONVERT(VARCHAR, DATEADD(ms, -3, DATEADD(mm, DATEDIFF(m, 0, th.transactiondt) + 1, 0)), 101)
,CASE
WHEN pd.TransactionEffDate < CAST(CONVERT(VARCHAR, pd.TransactionDate, 101) AS SMALLDATETIME) THEN CONVERT(VARCHAR, pd.TransactionDate, 101)
WHEN pd.TransactionEffDate < pd.EffectiveDate THEN CONVERT(VARCHAR, pd.EffectiveDate, 101)
ELSE CONVERT(VARCHAR, pd.TransactionEffDate, 101)
END
) lc
ORDER BY lc.[Date], lc.[Carrier], lc.[Direct Ceded Written Premium]
With the parameter that I have, it would only show up until November. However, I would like it to show the whole year, up to December at in this case, even if there are no data there since I didn't pick the enddate variable to be december. I attached an example screenshot of what it should look like when exported to excel.
Just to give you an idea:
declare #tbl TABLE(ID INT IDENTITY,SomeValue VARCHAR(100),SomeDate DATE);
INSERT INTO #tbl VALUES('Some date in March',{d'2016-03-05'}),('Some date in June',{d'2016-06-30'});
WITH AllMonths AS
(
SELECT 1 AS MonthIndex
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 7
UNION ALL SELECT 8
UNION ALL SELECT 9
UNION ALL SELECT 10
UNION ALL SELECT 11
UNION ALL SELECT 12
)
SELECT MonthIndex
,t.*
FROM AllMonths
LEFT JOIN #tbl AS t ON MONTH(t.SomeDate)=MonthIndex
The result
1 NULL NULL NULL
2 NULL NULL NULL
3 1 Some date in March 2016-03-05
4 NULL NULL NULL
5 NULL NULL NULL
6 2 Some date in June 2016-06-30
7 NULL NULL NULL
8 NULL NULL NULL
9 NULL NULL NULL
10 NULL NULL NULL
11 NULL NULL NULL
12 NULL NULL NULL
There are many ways to create a tally table
CTE with ROW_NUMBER()
A list like in my example
A physical table
It is a good idea to maintain a numbers/DATE table!
In a previous answer I showed one way to create such a table.

How to combine multiple rows of employee into single row in SQL Server

I'm having following table and below is expected results. Please let me known if there is a easy to way to get the expected results in SQL server.
EmpNo Name Benefit StartDate Status
--------------------------------------------
0001 ABC Medical 01/01/2014 Active
0001 ABC Dental 02/02/2013 Inactive
0001 ABC Vision 03/03/2012 Active
0002 XYZ Medical 01/01/2014 Active
0002 XYZ Dental 02/02/2008 Inactive
The results should be like below
EmpNo Name MedicalStart MedStatus DenStart DenStatus VisionStart VisStatus
---------------------------------------------------------------------------------------
0001 ABC 01/01/2014 Active 02/02/2013 Inactive 03/03/2012 Active
0002 XYZ 01/01/2014 Active 02/02/2008 Inactive .
I forgot put a few notes in my initial post.
1) There are 10 benefit plans available, so an employee may enroll for any number of plans up to ten (all plan or few plan or no plans at all).
2) There will be only one row with same benefit plan per EmpNo/Name.
3) Also, there are several fields associated with each row, for example, election option (Self, Family, etc) and many more. To make it simple, I have not included in the question.
Sample data:
CREATE TABLE #Test
(
EmpNo INT
, Name VARCHAR(255)
, Benefit VARCHAR(255)
, StartDate DATETIME2
, Status VARCHAR(255)
);
INSERT INTO #Test
(EmpNo, Name, Benefit, StartDate, Status)
VALUES
(0001, 'ABC', 'Medical', '01/01/2014', 'Active')
, (0001, 'ABC', 'Dental', '02/02/2013', 'Inactive')
, (0001, 'ABC', 'Vision', '03/03/2012', 'Active')
, (0002, 'XYZ', 'Medical', '01/01/2014', 'Active')
, (0002, 'XYZ', 'Dental', '02/02/2008', 'Inactive')
And a simple group clause:
Actual query (if there are historical records), using ROW_NUMBER will let you find latest record for each User and its Benefit:
SELECT T.EmpNo
, T.Name
, MAX(CASE WHEN T.Benefit = 'Medical ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS MedStart
, MAX(CASE WHEN T.Benefit = 'Medical' THEN T.Status END) AS MedStatus
, MAX(CASE WHEN T.Benefit = 'Dental ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS DenStart
, MAX(CASE WHEN T.Benefit = 'Dental' THEN T.Status END) AS DenStatus
, MAX(CASE WHEN T.Benefit = 'Vision ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS VisStart
, MAX(CASE WHEN T.Benefit = 'Vision' THEN T.Status END) AS VisStatus
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY EmpNo, Name, Benefit ORDER BY StartDate DESC) AS RowNo
, EmpNo
, Benefit
, Name
, StartDate
, Status
FROM #Test
) AS T
WHERE T.RowNo = 1
GROUP BY T.EmpNo
, T.Name
Query using dynamic SQL if there is unknown amount of Benefits. Might not be very efficient:
DECLARE #SQL NVARCHAR(MAX) = 'SELECT T.EmpNo, T.Name'
, #Benefit VARCHAR(MAX);
SELECT #SQL += ', MAX(CASE WHEN T.Benefit = ''' + Benefit + ''' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS ' + LEFT(Benefit, 3) + 'Star
, MAX(CASE WHEN T.Benefit = ''' + Benefit + ''' THEN T.Status END) AS ' + LEFT(Benefit, 3) + 'Status'
FROM (SELECT DISTINCT Benefit FROM #Test) AS T
SET #SQL += '
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY EmpNo, Name, Benefit ORDER BY StartDate DESC) AS RowNo, EmpNo, Benefit, NAME, StartDate, STATUS
FROM #Test
) AS T
WHERE T.RowNo = 1
GROUP BY T.EmpNo, T.Name'
EXEC sp_executesql #SQL
Query (if there are no historical records):
SELECT T.EmpNo
, T.Name
, MAX(CASE WHEN T.Benefit = 'Medical ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS MedStart
, MAX(CASE WHEN T.Benefit = 'Medical' THEN T.Status END) AS MedStatus
, MAX(CASE WHEN T.Benefit = 'Dental ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS DenStart
, MAX(CASE WHEN T.Benefit = 'Dental' THEN T.Status END) AS DenStatus
, MAX(CASE WHEN T.Benefit = 'Vision ' THEN CONVERT(VARCHAR(10), CONVERT(DATE, T.StartDate, 106), 103) END) AS VisStart
, MAX(CASE WHEN T.Benefit = 'Vision' THEN T.Status END) AS VisStatus
FROM #Test AS T
GROUP BY T.EmpNo
, T.Name
Output:
EmpNo Name MedStart MedStatus DenStart DenStatus VisStart VisStatus
-------------------------------------------------------------------------------------
1 ABC 01/01/2014 Active 02/02/2013 Inactive 03/03/2012 Active
2 XYZ 01/01/2014 Active 02/02/2008 Inactive NULL NULL
PIVOT solution on StartDate field:
DECLARE #tb AS TABLE
(
EmpNo INT
,Name NVARCHAR(25)
,Benefit NVARCHAR(25)
,StartDate DATE
,[Status] NVARCHAR(25)
);
INSERT INTO #tb VALUES (1, 'ABC', 'Medical', '01/01/2014', 'Active');
INSERT INTO #tb VALUES (1, 'ABC', 'Dental', '02/02/2013', 'Inactive');
INSERT INTO #tb VALUES (1, 'ABC', 'Vision', '03/03/2012', 'Active');
INSERT INTO #tb VALUES (2, 'XYZ', 'Medical', '01/01/2014', 'Active');
INSERT INTO #tb VALUES (2, 'XYZ', 'Dental', '02/02/2012', 'Inactive');
SELECT EmpNo
,Name
,MAX(MedicalStart) AS MedicalStart
,MAX(MedStatus) AS MedStatus
,MAX(DenStart) AS DenStart
,MAX(DenStatus) AS DenStatus
,MAX(VisionStart) AS VisionStart
,MAX(VisStatus) AS VisStatus
FROM
(
SELECT EmpNo
,Name
,[Medical] AS MedicalStart
,CASE
WHEN [Medical] IS NOT NULL AND [Status] = 'Active' THEN 'Active'
WHEN [Medical] IS NOT NULL AND [Status] = 'Inactive' THEN 'Inactive'
ELSE NULL END AS MedStatus
,[Dental] AS DenStart
,CASE
WHEN [Dental] IS NOT NULL AND [Status] = 'Active' THEN 'Active'
WHEN [Dental] IS NOT NULL AND [Status] = 'Inactive' THEN 'Inactive'
ELSE NULL END AS DenStatus
,[Vision] AS VisionStart
,CASE
WHEN [Vision] IS NOT NULL AND [Status] = 'Active' THEN 'Active'
WHEN [Vision] IS NOT NULL AND [Status] = 'Inactive' THEN 'Inactive'
ELSE NULL END AS VisStatus
,[Status]
FROM #tb
PIVOT
(
MAX(StartDate)
FOR Benefit IN ([Medical], [Dental], [Vision])
) AS pivotTableDate
) AS tb
GROUP BY EmpNo, Name;
You can check this link 'PIVOT on two or more fields in SQL Server' for information about full PIVOT solution.
I assume that for each employee you have at most 1 row for Medical, at most 1 row for Dental, and at most 1 row for Vision. If so, you can do it like this:
select
t.EmpNo, t.Name,
tMedical.MedicalStart, tMedical.MedicalStatus
from
(
select
EmpNo, Name
from
TableName
group by EmpNo, Name
) t
left outer join
(
select
EmpNo, Name, Benefit,
min(StartDate) as MedicalStart,
min(Status) as MedicalStatus
from
TableName
where
Benefit = 'Medical'
group by EmpNo, Name, Benefit
) tMedical on t.EmpNo = tMedical.EmpNo and t.Name = tMedical.Name
left outer join ...
Analogically to tMedical, you can add here left joins to tDental and tVision. And that should be it.
You can use Outer Apply to do this. I am assuming that one employee can have any number of medical, dental and vision rows. This query will take the latest StartDate for each type.
Select EmpNo, Name, Medical.StartDate MedicalStart, Medical.Status MedStatus, Dental.StartDate DenStart, Dental.Status DenStatus, Vision.StartDate VisionStart, Vision.Status VisStatus
From (Select Distinct EmpNo, Name From TableName) Emp
Outer Apply (Select Top 1 StartDate, Status From TableName Med Where Benefit='Medical' and Med.EmpNo=Emp.EmpNo Order By StartDate Desc) Medical
Outer Apply (Select Top 1 StartDate, Status From TableName Den Where Benefit='Dental' and Den.EmpNo=Emp.EmpNo Order By StartDate Desc) Dental
Outer Apply (Select Top 1 StartDate, Status From TableName Vis Where Benefit='Vision' and Vis.EmpNo=Emp.EmpNo Order By StartDate Desc) Vision
Let me know if this works for you...

SQL cross tab pivot query

I have a table as below.
CaseID StatusID StageID CaseRegisterTime City
1 1 5 datetime XYZ
2 1 5 datetime ABC
Now I want its Citywise count and only for only specific dates, and also in condition for statusid = 1 and stageid = 5.
Cities CurrentDate-1 CurrentDate-2 January2012-CurrentDate-3
XYZ 5 51 5008
JUS 0 0 125
ABC 1 0 48
I want my header to group cases for CaseRegisterTime as shown above.
Please help.
Use case when to convert your dates of interest to 'CurrentDate-1' and 'CurrentDate-2', and then pivot the results, using this strings as the new columns.
Alternatively, you can do something like this:
select City, sum(Date1) as Date1, sum(Date2) as Date2
from(
select City,
case when CaseRegisterTime='2012-01-01' then 1 else 0 end as Date1,
case when CaseRegisterTime='2012-15-01' then 1 else 0 end as Date2
from sample
) as T
group by City
you'd also have to filter out the registers which doesn't have the desired date.
Here's one of many ways to do it in SQL Server 2008 (using the Date datatype):
select distinct a.City as Cities
, (select count(*)
from MyTable
where CaseRegisterTime >= cast(getdate() - 1 as date)
and CaseRegisterTime < cast(getdate() - 0 as date)
and StatusID = a.StatusID
and StageID = a.StageID
and City = a.City
) as [CurrentDate-1]
, (select count(*)
from MyTable
where CaseRegisterTime >= cast(getdate() - 2 as date)
and CaseRegisterTime < cast(getdate() - 1 as date)
and StatusID = a.StatusID
and StageID = a.StageID
and City = a.City
) as [CurrentDate-2]
, (select count(*)
from MyTable
where CaseRegisterTime >= cast('20120101' as date)
and CaseRegisterTime < cast(getdate() - 2 as date)
and StatusID = a.StatusID
and StageID = a.StageID
and City = a.City
) as [January2012-CurrentDate-3]
from MyTable a
where a.StatusID = 1
and a.StageID = 5
Update
The case and sum method #JotaBe uses is about twice as fast on my box (with many less scans and reads), so here's what that could look like:
select a.City as Cities
, sum(a.[CurrentDate-1]) as [CurrentDate-1]
, sum(a.[CurrentDate-2]) as [CurrentDate-2]
, sum(a.[January2012-CurrentDate-3]) as [January2012-CurrentDate-3]
from (
select City
, case when CaseRegisterTime >= cast(getdate() - 1 as date)
and CaseRegisterTime < cast(getdate() - 0 as date)
then 1 else 0 end [CurrentDate-1]
, case when CaseRegisterTime >= cast(getdate() - 2 as date)
and CaseRegisterTime < cast(getdate() - 1 as date)
then 1 else 0 end [CurrentDate-2]
, case when CaseRegisterTime >= cast('20120101' as date)
and CaseRegisterTime < cast(getdate() - 2 as date)
then 1 else 0 end [January2012-CurrentDate-3]
from MyTable
where StatusID = 1
and StageID = 5
) as a
group by a.City
Something like this will do:
begin tran;
go
create table #t1(
ID int identity,
City varchar,
RegisterDate datetime
);
declare #firstDate datetime, #secondDate datetime;
set #firstDate = '2012-1-1';
set #secondDate = '2012-1-2';
insert into #t1 values
('A', #firstDate),
('A', #firstDate),
('B', #firstDate),
('B', #firstDate),
('B', #firstDate),
('A', #secondDate),
('A', #secondDate),
('A', #secondDate),
('B', #secondDate),
('B', #secondDate);
select * from #t1;
select pvt.*
from(
select ID, City, RegisterDate
from #t1
) a
pivot(
count(a.ID)
for a.RegisterDate in ([2012-1-1], [2012-1-2])
) as pvt;
drop table #t1;
go
rollback tran;