Calculate total business working days between two dates - sql

select count(distinct(dateadd(d, 0, datediff(d, 0,checktime)))) as workingdays
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>='2014-05-01' and checktime<='2014-05-30'
from the above code I am able to find total working days of employee between two dates.
workingdays
20
but now I want other column name total business days. I want to calculate total business days between two dates.
workingdays businessdays
20 21
how can i do this?

If you only want to exclude weekends then you can simply just exclude these using a conditional count by adding:
count(distinct case when datepart(weekday, getdate()) <= 5 then date end)
So your query becomes:
set datefirst 1;
select count(distinct(dateadd(d, 0, datediff(d, 0,checktime)))) as workingdays,
count(distinct case when datepart(weekday, getdate()) <= 5
then dateadd(d, 0, datediff(d, 0,checktime))
end) as weekdays
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>='2014-05-01' and checktime<='2014-05-30'
HOWEVER I would really recommend adding a calendar table to your database. It makes everything so easy, your query would become:
SELECT DaysWorked = COUNT(cio.Date),
WeekDaysWorked = COUNT(CASE WHEN c.IsWeekDay = 1 THEN cio.Date END),
WorkingDaysWorked = COUNT(CASE WHEN c.IsWorkingDay = 1 THEN cio.Date END),
TotalDays = COUNT(*),
TotalWeekDays = COUNT(CASE WHEN c.IsWeekDay = 1 THEN 1 END),
TotalWorkingDays = COUNT(CASE WHEN c.IsWorkingDay = 1 THEN 1 END)
FROM dbo.Calender AS c
LEFT JOIN
( SELECT DISTINCT
Date = CAST(CheckTime AS DATE)
FROM dbo.Departments AS d
CROSS JOIN dbo.userInfo AS ui
INNER JOIN dbo.CheckInOut AS cio
ON cio.UserID = ui.UserID
WHERE ui.Name = 'Gokul Gopalakrishnan'
AND d.deptname = 'GEN/SUP-TBL'
) AS cio
ON c.Date = cio.Date
WHERE d.Date >= '2014-05-01'
AND d.Date <= '2014-05-30';
This way you can define public holidays, weekends, etc. It is so much more flexible than any other solution.
EDIT
I think I misunderstood your original criteria. This should work for you with no calendar table:
SET DATEFIRST 1;
DECLARE #StartDate DATE = '2014-05-01',
#EndDate DATE = '2014-05-30';
DECLARE #Workdays INT =
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
-(DATEDIFF(WEEK, #StartDate, #EndDate) * 2)
-(CASE WHEN DATEPART(WEEKDAY, #StartDate) = 7 THEN 1 ELSE 0 END)
-(CASE WHEN DATEPART(WEEKDAY, #EndDate) = 6 THEN 1 ELSE 0 END);
SELECT WorkingDays = COUNT(DISTINCT CAST(CheckTime AS DATE)),
BusinessDays = #Workdays
FROM dbo.Departments AS d
CROSS JOIN dbo.userInfo AS ui
INNER JOIN dbo.CheckInOut AS cio
ON cio.UserID = ui.UserID
WHERE ui.Name = 'Gokul Gopalakrishnan'
AND d.deptname = 'GEN/SUP-TBL'
AND cio.CheckTime >= #StartDate
AND cio.CheckTime <= #EndDate;

following query calculate Fridays count between #FromDate and #ToDate variable
((DATEDIFF(DAY,#FromDate,#ToDate)-(6-DATEPART(dw,#FromDate)))/7)*2
Following query calculate Working day count and business day count between to date :
DECLARE #FromDate DATE = '2014-05-01',
#ToDate DATE = '2014-05-30'
SELECT COUNT(DISTINCT CAST(checktime AS Date)) as workingdays,
DATEDIFF(DAY,#FromDate,#ToDate) -
((DATEDIFF(DAY,#FromDate,#ToDate)-(6-DATEPART(dw,#FromDate)))/7)*2 AS BusinessDay
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>= #FromDate and checktime<=#ToDate

Related

How to get the sum of values in a table interval?

The question is very simple :-). I'm a beginner.
tables
Declare #startDate date,
Declare #endDate date
Select
d.ID,
d.Date as DateDocumet,
dt.id As TypeDocument,
p.Name as ProductName,
p.Price as Price,
d.qty
from Documents d
LEFT join product p on d.ProductId = p.id
LEFT join DocumentType dt on d.DocumentTypeId = dt.id
Result:
A taskā€¦..
There are two date variables.
How to get the sum of values(qty) between dates(#startDate - #endDate).
How to get the sum of values(qty) up to #startDate.
How get the sum of values(qty) down to #endDate.
If DocumentType is 1. Then the value(qty) minus.
Looks like you need conditional aggregation SUM(CASE WHEN....
CROSS APPLY also makes it easier to pre-calculate the negative qty
Declare #startDate date;
Declare #endDate date;
Select
SUM(CASE WHEN d.Date < #startDate THEN QtyToSum END),
SUM(CASE WHEN d.Date >= #startDate AND d.Date < #endDate THEN QtyToSum END),
SUM(CASE WHEN d.Date >= #endDate THEN QtyToSum END),
from Documents d
CROSS APPLY (VALUES (CASE WHEN d.DocumentTypeId = 1 THEN -d.qty ELSE d.qty END) ) AS v(QtyToSum)

Adding between dates - sql query

Currently I have a query that will get me the data from the pervious month .
I need to change thisso I can choose a selected date range that could allow me to input 2 dates and pull back all results between them.
See my query below:
select * from Table1 I
inner join Service K on I.Service_Key = K.Service_Key
inner join Status S on I.Status_Key = S.Status_Key
where K.Service_Key = '1'
and S.Status_Name = 'Closed'
and month(I.Date_Key) = (select case when Month (GETDATE())-1 = 0 then 12 else Month (GETDATE())-1 end)
and year(I.Date_Key) = (select case when Month (GETDATE()) -1 = 0 then year (GETDATE()) -1 ELSE YEAR (GETDATE()) end)
I need to be able to say where dates between dd/mm/yy and dd/mm/yy
You could declare the dates a variables:
Declare #Startdate as datetime
Declare #Enddate as datetime
set #Startdate = '01-AUG-20'
set #Enddate = '22-OCT-20'
select * from Table1 I
inner join Service K on I.Service_Key = K.Service_Key
inner join Status S on I.Status_Key = S.Status_Key
where K.Service_Key = '1'
and S.Status_Name = 'Closed'
and I.Date_Key > #Startdate
and I.Date_Key < #Enddate
A simple method is:
where K.Service_Key = '1' and
S.Status_Name = 'Closed' and
datediff(month, i.Date_key, getdate()) = 1
That version, however, cannot use an index on i.Date_Key if that is appropriate. A more index friendly version is:
where K.Service_Key = '1' and
S.Status_Name = 'Closed' and
i.Date_key < datefromparts(year(getdate()), month(getdate()), 1) and
i.Date_key >= dateadd(month, 1, datefromparts(year(getdate()), month(getdate()), 1))

How to check a SQL CASE with multiple conditions?

I have two tables. Accounts ACC and FinancialTrans FT
The FinancialTrans table is as follows:
AcctID TransTypeCode DateOfTrans
123 TOLL 2016-06-06 00:00:00.000
123 TOLL 2016-06-02 00:00:00.000
123 TOLL 2016-04-28 00:00:00.000
123 PYMT 2016-03-11 00:00:00.000
123 TOLL 2015-12-22 00:00:00.000
123 TOLL 2015-12-22 00:00:00.000
The requirement is:
When any Accounts have NO 'TOLL' or 'PYMT' in the last 2 years print 'Flag'
SELECT ACC.Field1
,ACC.Field2
,ACC.Field3
,ACC.Field4
,CASE WHEN
(SELECT Max(DateOfTrans) FROM FinanceTrans FT
WHERE ACC.AccountID = FT.AcctID
AND (TransTypeCode = 'TOLL' AND DateOfTrans >= DATEADD(year, -2, GETDATE()))
AND (TransTypeCode = 'PYMT' AND DateOfTrans >= DATEADD(year, -2, GETDATE()))
GROUP BY AcctID, TransTypeCode) IS NULL
THEN 'Flag'
ELSE ''
AND AS NoNo_Flag
FROM Accounts ACC
WHERE Condition 1, Condition 2...
try this one:
SELECT
acc.*,
CASE WHEN f.acctid IS NULL THEN 'flag' ELSE '' END AS flag_noTollOrPmt
FROM
accounts acc LEFT OUTER JOIN
(SELECT
AcctID,
MAX(DateOfTrans) AS max_dateOfTrans_TollOrPmt
FROM
FinanceTrans
WHERE
DateOfTrans >= DATEADD(YEAR, -2, GETDATE()) AND
TransTypeCode IN( 'TOLL' , 'PYMT')
GROUP BY
AcctID) f ON
acc.acctid = f.acctid
You should be using window functions. The logic is to look at the maximum date for the two transaction types. The flag then depends on the relationship to the current date.
select a.*,
(case when max(case when transtype in ('TOLL', 'PYMT') then DateOfTrans end) over
(partition by acctid) >= dateadd(year, -2, getdate())
then 0 else 1
end) as flag
from accounts;
I could be misunderstanding the question, in which case I can refine my answer. It seems like you just need to check for the existence, or not, of records in the the sub-query. So to that extent do you really need to do an aggregate? And, try using EXISTS:
SELECT ACC.Field1, ACC.Field2, ACC.Field3, ACC.Field4,
CASE WHEN NOT EXISTS
(SELECT DateOfTrans
FROM FinanceTrans FT
WHERE ACC.AccountID = FT.AcctID
AND (TransTypeCode = 'TOLL' AND DateOfTrans >= DATEADD(year, -2, GETDATE()))
AND (TransTypeCode = 'PYMT' AND DateOfTrans >= DATEADD(year, -2, GETDATE())))
THEN 'Flag'
ELSE ''
END AS NoNo_Flag
FROM Accounts ACC
WHERE [*condition*]
So this is how I resolved this issue:
I created a separate column for each, first and then stored those details in a temporary table.
Then I pulled data from the temporary table using conditions to create the flag.
My code is as follows:
SELECT ACC.Field1
,ACC.Field2
,ACC.Field3
,ACC.Field4
,(SELECT Max(DateOfTrans) FROM FinanceTrans FT
WHERE ACC.AccountID = FT.AcctID
AND TransTypeCode = 'TOLL'
GROUP BY AcctID, TransTypeCode) LastTollDate
,(SELECT Max(DateOfTrans) FROM FinanceTrans FT
WHERE ACC.AccountID = FT.AcctID
AND TransTypeCode = 'PYMT'
GROUP BY AcctID, TransTypeCode) LastPymtDate
INTO #Temp_Data
FROM Accounts ACC
WHERE Condition 1, Condition 2...
SELECT ACC.Field1
,ACC.Field2
,ACC.Field3
,ACC.Field4
,CASE WHEN LastTollDate >= DATEADD(year, -2, GETDATE())
AND LastPymtDate >= DATEADD(year, -2, GETDATE())
THEN 'Flag'
ELSE ''
END AS Flag
FROM #Temp_Data

SQL IN and NOT IN alternative?

I am trying to find a way to optimize my following SQL query which is taking a long time to run:
(s.StaffID in (Select sch.StaffID From Schedule sch
where sch.AccountID=#AccountID
and sch.Date Between DATEADD(day, -90, #Today) and #Today
and sch.Status<>2))
AND
(s.StaffID Not in (Select sch.StaffID From Schedule sch
where sch.AccountID=#AccountID
and sch.Date Between DATEADD(day, -90, #Today) and #Today
and sch.Status=2))
Can I replace it with another simple query which does less work?
You can use aggregation to combine the two expressions:
s.staffid in
(
select staffid
from schedule
where accountid = #accountid
and date between dateadd(day, -90, #today) and #today
group by staffid
having count(case when status <> 2 then 1 end) > 0
and count(case when status = 2 then 1 end) = 0
)
I'd move this logic into sub-query like this:
select s.StaffID
from StaffID as s inner join (
Select StaffID
From Schedule
where
AccountID=#AccountID and
Date Between DATEADD(day, -90, #Today) and #Today and
group by StaffID
having max(case when Status=2 then 2 else 1 end) = 1
) as t
on (s.StaffID = t.StaffID)
So, condition in having will filter out members who had status==2 in past 90 days

Revenue for two months date wise

I am trying to get data for last 2 month ...but the query does not give perfect result....
SELECT DAY(table_A.PaymentDate) as date1 ,
(case when MONTH(table_A.PaymentDate) = MONTH(CURRENT_TIMESTAMP) - 1
then CAST(SUM(table_A.Total_Amount) AS INT)
else 0
end) AS last_month_CNT,
(case when MONTH(table_A.PaymentDate) = MONTH(CURRENT_TIMESTAMP)
then CAST(SUM(table_A.Total_Amount) As INT)
else 0
end) as This_month_CNT
FROM Tbl_Pan_Paymentdetails table_A
FULL OUTER JOIN Tbl_Pan_Paymentdetails table_B
ON table_A.PaymentDate=table_B.PaymentDate
WHERE YEAR(table_A.PaymentDate) = YEAR(CURRENT_TIMESTAMP)
AND
table_A.PaymentDate >= DATEADD(MONTH, -1, GETDATE())
GROUP BY
DAY(table_A.PaymentDate) ,MONTH(table_A.PaymentDate)
order by
DAY(table_A.PaymentDate);
Move the entire case expression inside the sum function and don't include the month in the group by. Also, the full outer join seems unnecessary so I removed it.
This should be what you are looking for:
SELECT
DAY(PaymentDate) as date1 ,
SUM(CASE WHEN MONTH(PaymentDate) = MONTH(CURRENT_TIMESTAMP)-1 THEN CAST(Total_Amount AS INT) ELSE 0 END) AS last_month_CNT,
SUM(CASE WHEN MONTH(PaymentDate) = MONTH(CURRENT_TIMESTAMP) THEN CAST(Total_Amount AS INT) ELSE 0 END) AS This_month_CNT
FROM Tbl_Pan_Paymentdetails
WHERE YEAR(PaymentDate) = YEAR(CURRENT_TIMESTAMP)
AND PaymentDate >= DATEADD(MONTH, -1, GETDATE())
GROUP BY DAY(PaymentDate)
ORDER BY DAY(PaymentDate);