Calculating number of days between Admission Date and today - sql

I found the following in Stackoverflow and have been working with it to find the number of Patient Days each month. It works very well if I have both the Admit and Discharge dates.
I can't figure out how to edit it to calculate the Patient days when the discharge date has not been completed... the patient is still in the hospital. If feels like I should use Coalesce or ISNULL to find the records where the Discharge Date is NULL, but I'm not a programmer and would appreciate your help.
WITH Mos AS (
SELECT
D.ED_ADMIT_DATE,
D.ED_DISCHARGE_DATE,
Number,
DateAdd(Month, Number, D.ED_ADMIT_DATE - Day(D.ED_ADMIT_DATE) + 1) MoDate
FROM
cases_cstm D
INNER JOIN master.dbo.spt_values V ON V.Number <= DateDiff(Month, D.ED_ADMIT_DATE, D.ED_DISCHARGE_DATE)
WHERE
V.Type = 'P'), Dys AS (
SELECT
MoDate,
DateDiff(
Day,
CASE WHEN Number = 0 THEN ED_ADMIT_DATE ELSE MoDate END,
CASE WHEN Number = DateDiff(Month, ED_ADMIT_DATE, ED_DISCHARGE_DATE) THEN ED_DISCHARGE_DATE ELSE DateAdd(Month, 1, MoDate) -1
END
) + 1 Cnt
FROM Mos)
SELECT Year(MoDate) Yr,
Coalesce(DateName(Month, MoDate), 'Total') Mo,
Convert(varchar(11), Sum(Cnt)) + ' day' + CASE WHEN Sum(Cnt) = 1 THEN '' ELSE 's' END Descr
FROM Dys
GROUP BY MoDate
WITH ROLLUP
ORDER BY
Grouping(MoDate),
MoDate;

You could replace each occurance of ED_DISCHARGE_DATE with:
IsNull(ED_DISCHARGE_DATE,getdate())
This uses the current time whenever the discharge date is unavailable.

Related

Calculate employee shift based on 1st login of the day

I want to calculate employees shift based on his system login time ,i am able to calculate shifts based on login times .now i want to calculate shift only for 1st login entry for given day if there are multiple logins for same day.Please help how i can do this .
**
SQL Query :
WITH shiftalloence AS
(
SELECT (timesheet.start_time_server) ,
users.first_name + ' ' + users.last_name AS employee_name ,
CASE
WHEN Dateadd(d, -Datediff(d, 0, dbo.[timesheet].start_time_server), dbo.[timesheet].start_time_server) BETWEEN '05:30:00' AND '08:00:00' THEN 'Morning'
WHEN Dateadd(d, -Datediff(d, 0, dbo.[timesheet].start_time_server), dbo.[timesheet].start_time_server) BETWEEN '08:01:00' AND '11:00:00' THEN 'General'
WHEN Dateadd(d, -Datediff(d, 0, dbo.[timesheet].start_time_server), dbo.[timesheet].start_time_server) BETWEEN '11:05:00' AND '18:00:00' THEN 'Evening'
ELSE '0'
END AS shift
FROM timesheet
JOIN users
ON timesheet.user_id=users.user_id
WHERE Month(start_time_server)=Month(Getdate())
AND Datename(weekday,timesheet.start_time_server) IN 'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday')
AND
start_time_server NOT IN
(
SELECT timesheet.start_time_server
FROM timesheet
JOIN leaves
ON timesheet.user_id=leaves.user_id
WHERE CONVERT(varchar(10),timesheet.start_time_server,111) =CONVERT(varchar(10),leaves.start_time_server,111)
AND task_type_name IN ('leave',
'Half Day') )), tt AS
(
SELECT shiftalloence.employee_name ,
count(
CASE
WHEN shift = 'Morning' THEN 1
ELSE NULL
END) AS 'Morning',
count(
CASE
WHEN shift = 'General' THEN 1
ELSE NULL
END) AS 'General' ,
count(
CASE
WHEN shift = 'Evening' THEN 1
ELSE NULL
END) AS 'Evening'
FROM shiftalloence
GROUP BY employee_name )SELECT *
FROM tt
**
Could you just simply distinct select and order by ASC or DESC depending on the dates? This would allow you to show just the shift/day/id you're wanting and showing you the first time they logged in because you're ordered it in the way you're wanting.
This works because if you get back 3 rows and order by the datetime asc/desc you can also simply do select top(1). It'll grab just the top record in that list for that shift/day/person.
Small example of what I am saying:
SELECT TOP 1 * FROM Employees
order by datetime asc;
Something like that.

Dividing monthly rate between Start and End Dates

I have Monthly rate for each employee in one table which I need to distribute equally based on the Employee Start and End date in the particular project.
For e.g. The monthly rate for employee X is $4300 which is stored in Table A. The employee start & end date in the project is '2017-11-03' & '2017-12-12' respectively and is stored in Table B. I need the employee cost for both Nov and Dec months.
Appreciate any help on this.
Edits:
Table 1:Employee Details
Col1-Employee name
Col2-Start date
Col3- End date
Table 2: Rate Card
Col1-Location
Col2-Grade
Col3- Cost Per Month
Table 3: Employee Billing
Col1-Year
Col2-Month
Col3- Employee
Col4- Cost per month
So if Employee worked from 4-Nov-17 to 31-Dec-17 and Cost per month is $4000, then in Billing table there should be 2 entries like:
2017,Nov,Emp1,(4000/30)*((30-4)+1)
2017,Dec,Emp1,4000
I was ale to insert 2 entries in table 3 based on input from table 1 but not able to calculate the customized cost
case
when (webResource_Details_ESS_recurrent_entries.recurrent_month<>DATEPART(mm, l.start_date) or
webResource_Details_ESS_recurrent_entries.recurrent_month<>DATEPART(mm, l.end_date))
then
l.Employee_Cost
when DATEPART(day, l.start_date) <> 1 and Eomonth(l.start_date) = Eomonth(l.end_date) then
(Datediff(day, l.start_date, l.end_date) *
(l.Employee_Cost / Datediff(day,Eomonth(l.start_date), Dateadd(month,1, Eomonth(l.start_date))) ) )
when DATEPART(day, l.start_date) <> 1 and Eomonth(l.start_date) <> Eomonth(l.end_date) then ( Datediff(day, l.start_date, Eomonth(l.start_date)) *
( l.Employee_Cost /Datediff(day, Eomonth(l.start_date), Dateadd(month, 1,Eomonth(l.start_date))) ) )
when DATEPART(day, l.end_date) <> (DATEPART(day, DATEADD(second,-1,DATEADD(month, DATEDIFF(month,0,l.end_date)+1,0)))) and Eomonth(l.start_date) <> Eomonth(l.end_date) then (Datediff(day, ( Dateadd(month, Datediff(month, 0,l.end_date),0) ),l.end_date)
*(l.Employee_Cost /Datediff(day, Eomonth(l.end_date),Dateadd(month, 1, Eomonth(l.end_date)))))
end as Employee_Cost
SELECT em.employeed,
CASE
WHEN Eomonth(startdate) = Eomonth(enddate)
THEN (Datediff(day, startdate, enddate) *
(rate / Datediff(day,Eomonth(startdate), Dateadd(month,1, Eomonth(startdate))) ) )
ELSE ( ( Datediff(day, startdate, Eomonth(startdate)) *
( rate /Datediff(day, Eomonth(startdate), Dateadd(month, 1,Eomonth(startdate))) ) ) +
(Datediff(day, ( Dateadd(month, Datediff(month, 0,enddate),0) ),enddate)
*(rate /Datediff(day, Eomonth(enddate),Dateadd(month, 1, Eomonth(enddate))))))
END AS wage
FROM employeemaster em
INNER JOIN employeerate AS er
ON ( em.employeed = er.employeed )
First Case will handle case when both start date and end date are in same month
eomonth(startdate) = eomonth(enddate) then (datediff(day,startdate,enddate)*(rate/datediff(day, eomonth(startdate), dateadd(month, 1, eomonth(startdate)))))
This calculate days and rate for month of start date
datediff(day,startdate,eomonth(startdate)) * (rate/datediff(day, eomonth(startdate), dateadd(month, 1, eomonth(startdate)))))
This calculate days and rate for month of end date
(datediff(day,(DATEADD(month, DATEDIFF(month, 0, enddate), 0)),enddate)* rate/datediff(day, eomonth(enddate), dateadd(month, 1, eomonth(enddate))))

How to get week formatted string in sql query

I have the following SQL query (sql server 2008):
SELECT sum(data.freq) as freq,data.week as week FROM (
SELECT
count(daterequested) as freq,
datepart(wk,daterequested) as week,
daterequested
FROM request ma
JOIN contracts mc ON (mc.uid= ma.uid)
JOIN groups og ON og.groupuid = mc.groupuid
JOIN member m ON (m.memberuid = mc.memberuid)
WHERE daterequested BETWEEN
DATEADD(MONTH,-1,GETDATE())
AND
GETDATE()
AND isdeleted = 0
GROUP BY datepart(wk,daterequested),daterequested
--ORDER BY daterequested ASC
) data
GROUP BY data.week
The result is a table with the following data:
Instead of showing the week number I would like to show the week formatted as following:
MM/dd where MM = month and dd is the day where the week starts.
It would be great if I can format starting with first day of the week, then a middle slash, then the last day of that week and finally the month: example: 11-17/04 (April 11 to 17), etc.
Here is the final table that I would like to get:
Any clue?
In case someone needs it just found a solution, maybe is not the best but works.
SELECT sum(data.freq) as freq,
data.week as week, CAST(data.weekstart as varchar) + '-' + CAST(data.weekend as varchar) + '/' + CAST(data.monthend as varchar) as formatweek
FROM (
SELECT
count(daterequested) as freq,
datepart(wk,daterequested) as week,
DATEPART(dd,DATEADD(dd, -(DATEPART(dw, daterequested)-1), daterequested)) weekstart,
DATEPART(dd,DATEADD(dd, 7-(DATEPART(dw, daterequested)), daterequested)) weekend,
DATEPART(mm,DATEADD(dd, 7-(DATEPART(dw, daterequested)), daterequested)) monthend,
daterequested
FROM requestma
JOIN contracts mc ON (mc.uid= ma.uid)
JOIN groups og ON og.groupuid = mc.groupuid
JOIN member m ON (m.memberuid = mc.memberuid)
WHERE daterequested BETWEEN
DATEADD(MONTH,-1,GETDATE())
AND
GETDATE()
AND isdeleted = 0
GROUP BY datepart(wk,daterequested),daterequested
--ORDER BY daterequested ASC
) data
GROUP BY data.week,data.weekstart,data.weekend,data.monthend

Select first and last record each day

I have a table with an engineerID, DateTimeCreated as DateTime, JobID and AuditTypeID
I need a query shows first (engineerID, JobID with AuditTypeID 1) and last (engineerID, JobID with AuditTypeID 2) on each row of the query.
SELECT TOP (100) PERCENT
dbo.AuditTrail.EngineerId,
dbo.AuditTrail.AuditTypeId,
dbo.Engineers.Name,
dbo.Engineers.EngineerTypeCode,
dbo.AuditTrail.JobId,
CAST(dbo.AuditTrail.DateTimeCreated AS Date) AS _Date
FROM
dbo.AuditTrail
INNER JOIN
dbo.Engineers
ON dbo.AuditTrail.EngineerId = dbo.Engineers.EngineerId
WHERE
(dbo.AuditTrail.AuditTypeId = 1) AND
(dbo.Engineers.EngineerTypeCode = 'p') AND
(dbo.Engineers.EngineerTypeCode = 'p') AND
(DATEPART(mm, dbo.AuditTrail.DateTimeCreated) = 6) AND
(DATEPART(YYYY, dbo.AuditTrail.DateTimeCreated) = 2014)
group by
AuditTrail.engineerID,
JobID,
AuditTypeId,
Engineers.name,
Engineers.EngineerTypeCode,
CAST(dbo.AuditTrail.DateTimeCreated AS Date)
ORDER BY
dbo.AuditTrail.EngineerID DESC
for the first part of my query. Unfortunatly I cannot see to select the first record for each day
Any help will be greatly appreciated
First just get the data you need, including the create date. Then grouping that data by date, select the min of each day. Finally, join the two sets, selecting only the minimum of each day -- that is, the first occurrence of each day.
with
AllMonth( EngineerId, AuditTypeId, Name, EngineerTypeCode, JobId, DateTimeCreated )as(
SELECT TOP (100) PERCENT
a.EngineerId,
a.AuditTypeId,
e.Name,
e.EngineerTypeCode,
a.JobId,
a.DateTimeCreated
FROM dbo.AuditTrail a
JOIN dbo.Engineers e
ON e.EngineerId = a.EngineerId
AND e.EngineerTypeCode = a.EngineerTypeCode
WHERE
a.AuditTypeId = 1
AND a.EngineerTypeCode = 'p'
AND a.DateTimeCreated >= DateAdd( mm, DateDiff( mm, 0, GetDate()), 0)
AND a.DateTimeCreated < DateAdd( mm, DateDiff( mm, 0, GetDate()) + 1, 0)
),
FirstByDay( MinDate )as(
select Min( DateTimeCreated )
from AllMonth
group by cast( DateTimeCreated AS Date )
)
select *
from AllMonth a
join FirstByDay f
on f.MinDate = a.DateTimeCreated
ORDER BY a.EngineerID DESC;
To get the last item of each day, just add a max to FirstByDay and add to the join. Work it into one long row if you really want to.
Btw, didn't I hear a few years back that the later versions of MSSQL ignored top (100) percent? I don't work with it much these days, and my memory is...well, just...somewhere around here...

PIVOT SQL Server Assistance

Given the following table structure:
CrimeID | No_Of_Crimes | CrimeDate | Violence | Robbery | ASB
1 1 22/02/2011 Y Y N
2 3 18/02/2011 Y N N
3 3 23/02/2011 N N Y
4 2 16/02/2011 N N Y
5 1 17/02/2011 N N Y
Is there a chance of producing a result set that looks like this with T-SQL?
Category | This Week | Last Week
Violence 1 3
Robbery 1 0
ASB 3 1
Where last week shuld be a data less than '20/02/2011' and this week should be greater than or equal to '20/02/2011'
I'm not looking for someone to code this out for me, though a code snippet would be handy :), just some advice on whether this is possible, and how i should go about it with SQL Server.
For info, i'm currently performing all this aggregation using LINQ on the web server, but this requires 19MB being sent over the network every time this request is made. (The table has lots of categories, and > 150,000 rows). I want to make the DB do all the work and only send a small amount of data over the network
Many thanks
EDIT removed incorrect sql for clarity
EDIT Forget the above try the below
select *
from (
select wk, crime, SUM(number) number
from (
select case when datepart(week, crimedate) = datepart(week, GETDATE()) then 'This Week'
when datepart(week, crimedate) = datepart(week, GETDATE())-1 then 'Last Week'
else 'OLDER' end as wk,
crimedate,
case when violence ='Y' then no_of_crimes else 0 end as violence,
case when robbery ='Y' then no_of_crimes else 0 end as robbery,
case when asb ='Y' then no_of_crimes else 0 end as asb
from crimetable) as src
UNPIVOT
(number for crime in
(violence, robbery, asb)) as pivtab
group by wk, crime
) z
PIVOT
( sum(number)
for wk in ([This Week], [Last Week])
) as pivtab
Late to the party, but a solution with an optimal query plan:
Sample data
create table crimes(
CrimeID int, No_Of_Crimes int, CrimeDate datetime,
Violence char(1), Robbery char(1), ASB char(1));
insert crimes
select 1,1,'20110221','Y','Y','N' union all
select 2,3,'20110218','Y','N','N' union all
select 3,3,'20110223','N','N','Y' union all
select 4,2,'20110216','N','N','Y' union all
select 5,1,'20110217','N','N','Y';
Make more data - about 10240 rows in total in addition to the 5 above, each 5 being 2 weeks prior to the previous 5. Also create an index that will help on crimedate.
insert crimes
select crimeId+number*5, no_of_Crimes, DATEADD(wk,-number*2,crimedate),
violence, robbery, asb
from crimes, master..spt_values
where type='P'
create index ix_crimedate on crimes(crimedate)
From here on, check output of each to see where this is going. Check also the execution plan.
Standard Unpivot to break the categories.
select CrimeID, No_Of_Crimes, CrimeDate, Category, YesNo
from crimes
unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
where YesNo='Y'
Notes:
The filter on YesNo is actually applied AFTER unpivoting. You can comment it out to see.
Unpivot again, but this time select data only for last week and this week.
select CrimeID, No_Of_Crimes, Category,
Week = sign(datediff(d,CrimeDate,w.firstDayThisWeek)+0.1)
from crimes
unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
cross join (select DATEADD(wk, DateDiff(wk, 0, getdate()), 0)) w(firstDayThisWeek)
where YesNo='Y'
and CrimeDate >= w.firstDayThisWeek -7
and CrimeDate < w.firstDayThisWeek +7
Notes:
(select DATEADD(wk, DateDiff(wk, 0, getdate()), 0)) w(firstDayThisWeek) makes a single-column table where the column contains the pivotal date for this query, being the first day of the current week (using DATEFIRST setting)
The filter on CrimeDate is actually applied on the BASE TABLE prior to unpivoting. Check plan
Sign() just breaks the data into 3 buckets (-1/0/+1). Adding +0.1 ensures that there are only two buckets -1 and +1.
The final query, pivoting by this/last week
select Category, isnull([1],0) ThisWeek, isnull([-1],0) LastWeek
from
(
select Category, No_Of_Crimes,
Week = sign(datediff(d,w.firstDayThisWeek,CrimeDate)+0.1)
from crimes
unpivot (YesNo for Category in (Violence,Robbery,ASB)) upv
cross join (select DATEADD(wk, DateDiff(wk, 0, getdate()), -1)) w(firstDayThisWeek)
where YesNo='Y'
and CrimeDate >= w.firstDayThisWeek -7
and CrimeDate < w.firstDayThisWeek +7
) p
pivot (sum(No_Of_Crimes) for Week in ([-1],[1])) pv
order by Category Desc
Output
Category ThisWeek LastWeek
--------- ----------- -----------
Violence 1 3
Robbery 1 0
ASB 3 3
I would try this:
declare #FirstDayOfThisWeek date = '20110220';
select cat.category,
ThisWeek = sum(case when cat.CrimeDate >= #FirstDayOfThisWeek
then crt.No_of_crimes else 0 end),
LastWeek = sum(case when cat.CrimeDate >= #FirstDayOfThisWeek
then 0 else crt.No_of_crimes end)
from crimetable crt
cross apply (values
('Violence', crt.Violence),
('Robbery', crt.Robbery),
('ASB', crt.ASB))
cat (category, incategory)
where cat.incategory = 'Y'
and crt.CrimeDate >= #FirstDayOfThisWeek-7
group by cat.category;