How Can I write in my code to exclude accounts that have had a payment posted w/in last 45 days? - sql

I'm working in SQL Server Mgmt Studio and I'm trying to identify accounts that have not had a payment posted since the last 45 days. I've tried declaring a min and max date with getdate being less than 45 days, but that didnt work. I've also tried stating "where last payment !> getdate() -45".... but this did not give me the results I was looking for. what I need the query to do is only identify accounts that truly have not had a payment post in the last 45 days. but I'm not sure how to state this in my where clause. Here's my query
DECLARE #minDate datetime;
DECLARE #maxDate datetime;
SET #minDate = GETDATE();
SET #maxDate = GETDATE() - 45;
SELECT DISTINCT
a.id,
a.FacilityCode AS [Facility],
a.accountUnit,
a.accountNum AS [Account Number],
a.amountBalance AS [Balance],
a.accountstatus AS [Status],
CONVERT(date, a.accountStatusDate) AS StatusDate,
pmtEntryDate AS [Pmt Entry Date],
v.LastLinkARpmtDate AS [Last Pmt Date],
CONVERT(date, a.accountnextactdate) AS NextActionDate,
CONVERT(date, a.rpcDate) AS RightpartyContactDate,
a.flag AS accountFlag,
pay.flag AS pmtFlag,
a.accounttype
FROM dbo.tbAccount a
JOIN dbo.tbpmtinfo pay ON a.id = pay.id
JOIN dbo.vLastPmtDate v ON v.id = a.id
FULL JOIN (SELECT id,
MAX(deDate) AS MFSLoadDate
FROM dbo.tbDataEvents (NOLOCK)
WHERE deNewVal = 'MFS'
GROUP BY id) m ON m.id = a.id
WHERE pay.flag IN ('D')
AND a.amountBalance > 0
AND a.FacilityCode IN ('PHKY', 'QANM', 'QAOH', 'QBCA', 'QBIL', 'QBTX', 'QCIL', 'QDAL', 'QEWY', 'QFAR', 'QFGA',
'QGIL', 'QHAR', 'QHIL', 'QHTN', 'QLAL', 'QLPA', 'QMIL', 'QMNC', 'QMNM', 'QMNV', 'QMOR',
'QMTN', 'QMTX', 'QMUT', 'QRIL', 'QRKY', 'QSPA', 'QTGA', 'QTKY', 'QUIL', 'QVIL', 'QWIL', 'LHFL')
AND a.amountBalance >= '1000'
AND a.accountStatus IN ('PA', 'PP', 'BPA')
AND a.flag = 'A'
AND a.accountType IN ('Resid', 'Slfpy')
--and LastLinkARpmtDate !> Getdate() -45 and accountNum = '40728597'
AND LastLinkARpmtDate BETWEEN #minDate AND #maxDate;

I would use a WHERE NOT EXISTS clause to eliminate recent activity:
SELECT DISTINCT a.id
,a.FacilityCode [Facility]
,a.accountUnit
,a.accountNum [Account Number]
,a.amountBalance [Balance]
,a.accountstatus [Status]
,convert(DATE, a.accountStatusDate) StatusDate
,pmtEntryDate [Pmt Entry Date]
,v.LastLinkARpmtDate [Last Pmt Date]
,convert(DATE, a.accountnextactdate) NextActionDate
,convert(DATE, a.rpcDate) RightpartyContactDate
,a.flag AS accountFlag
,pay.flag AS pmtFlag
,a.accounttype
FROM dbo.tbAccount AS a
JOIN dbo.tbpmtinfo AS pay ON a.id = pay.id
JOIN dbo.vLastPmtDate AS v ON v.id = a.id
FULL JOIN (
SELECT id
,Max(deDate) AS MFSLoadDate
FROM dbo.tbDataEvents(NOLOCK)
WHERE deNewVal = 'MFS'
GROUP BY id
) m ON m.id = a.id
WHERE pay.flag IN ('D')
AND a.amountBalance > 0
AND a.FacilityCode IN (
'PHKY'
,'QANM'
,'QAOH'
,'QBCA'
,'QBIL'
,'QBTX'
,'QCIL'
,'QDAL'
,'QEWY'
,'QFAR'
,'QFGA'
,'QGIL'
,'QHAR'
,'QHIL'
,'QHTN'
,'QLAL'
,'QLPA'
,'QMIL'
,'QMNC'
,'QMNM'
,'QMNV'
,'QMOR'
,'QMTN'
,'QMTX'
,'QMUT'
,'QRIL'
,'QRKY'
,'QSPA'
,'QTGA'
,'QTKY'
,'QUIL'
,'QVIL'
,'QWIL'
,'LHFL'
)
AND a.amountBalance >= '1000'
AND a.accountStatus IN (
'PA'
,'PP'
,'BPA'
)
AND a.flag = 'A'
AND a.accountType IN (
'Resid'
,'Slfpy'
)
AND NOT EXISTS (
SELECT NULL
FROM vLastPmtDate
WHERE v.id = a.id
AND CAST(v.LastLinkARpmtDate AS DATE) > CAST(dateadd(day, - 45, getdate() AS DATE))
)
AND accountNum = '40728597'
AND LastLinkARpmtDate BETWEEN #minDate
AND #maxDate;

The function dateadd() is what you need:
DATEADD (datepart , number , date)
Set #maxDate = DATEADD(DAY, -45, GETDATE());

Related

How do I show the percentage of a group in SQL?

I am trying to show the percentage of tasks completed before a certain deadline, for separate groups.
I have calculated the percentage for the whole, but when I try to split the data into a calculation for each group I keep getting errors.
To calculate the percentage, I have been using:
DECLARE #TM DATETIME;
SET #TM = DATEADD(MONTH,DATEDIFF(MONTH,'19000101', GETDATE()), '19000101')
SELECT CAST(
(
SELECT COUNT (a.[completed])
FROM [table] a
JOIN [other table] b
ON a.TaskID = b.TaskID
WHERE a.[completed] >= DATEADD(MONTH,0,#TM) AND
b.[completedByDeadline] = 1) AS DECIMAL (10,2))
/
CAST(
(
SELECT COUNT([completed])
FROM [table]
WHERE [completed] >= DATEADD(MONTH,0,#TM))
*100 AS Decimal (10,2))
When I try to add the [groupName] column to the SELECT list and a GROUP BY clause, there are errors; it mentions the need for an EXISTS keyword, but I can't see where to put one.
Any help that people could provide would be fantastic! Thanks.
I would just use this:
DECLARE #TM DATETIME;
SET #TM = DATEADD(MONTH,DATEDIFF(MONTH,'19000101', GETDATE()), '19000101')
SELECT
COUNT (case
when b.[completedByDeadline] = 1
then 1
else NULL
end) / COUNT (a.[completed])
FROM [table] a
LEFT JOIN [other table] b
ON a.TaskID = b.TaskID
WHERE a.[completed] >= DATEADD(MONTH,0,#TM)
I like using AVG() for this sort of query:
SELECT AVG(case when b.[completedByDeadline] = 1 then 1.0 else 0
end)
FROM [table] a LEFT JOIN
[other table] b
ON a.TaskID = b.TaskID
WHERE a.[completed] >= DATEADD(MONTH, 0, #TM);
If completedByDeadline only takes on the values 0 and 1, this can be simplified further:
SELECT AVG(b.[completedByDeadline] * 1.0)
FROM [table] a LEFT JOIN
[other table] b
ON a.TaskID = b.TaskID
WHERE a.[completed] >= DATEADD(MONTH, 0, #TM);
Note that these will return NULL if there are no corresponding tasks in b.

Display Month Gaps for Each location

I have the following query which takes in the opps and calculates the duration, and revenue for each month. However, for some locations, where there is no data, it is missing some months. Essentially, I would like all months to appear for each of the location and record type. I tried a left outer join on the calendar but that didn't seem to work either.
Here is the query:
;With DateSequence( [Date] ) as
(
Select CAST(#fromdate as DATE) as [Date]
union all
Select CAST(dateadd(day, 1, [Date]) as Date)
from DateSequence
where Date < #todate
)
INSERT INTO CalendarTemp (Date, Day, DayOfWeek, DayOfYear, WeekOfYear, Month, MonthName, Year)
Select
[Date] as [Date],
DATEPART(DAY,[Date]) as [Day],
DATENAME(dw, [Date]) as [DayOfWeek],
DATEPART(DAYOFYEAR,[Date]) as [DayOfYear],
DATEPART(WEEK,[Date]) as [WeekOfYear],
DATEPART(MONTH,[Date]) as [Month],
DATENAME(MONTH,[Date]) as [MonthName],
DATEPART(YEAR,[Date]) as [Year]
from DateSequence option (MaxRecursion 10000)
;
DELETE FROM CalendarTemp WHERE DayOfWeek IN ('Saturday', 'Sunday');
SELECT
AccountId
,AccountName
,Office
,Stage = (CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
,Id
,Name
,RecordType= (CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,Start_Date
,End_Date
,Probability
,Estimated_Revenue_Won = ISNULL(Amount, 0)
,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) AS Row
--,Revenue_Per_Day = CAST(ISNULL(Amount/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0),0) as money)
,YEAR(c.Date) as year
,MONTH(c.Date) as Month
,c.MonthName
--, ISNULL(CAST(Sum((Amount)/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0)) as money),0) As RevenuePerMonth
FROM SF_Extracted_Opps o
LEFT OUTER JOIN CalendarTemp c on o.Start_Date <= c.Date AND o.End_Date >= c.Date
WHERE
Start_Date <= #todate AND End_Date >= #fromdate
AND Office IN (#Location)
AND recordtypeid IN ('LAS1')
GROUP BY
AccountId
,AccountName
,Office
,(CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
,Id
,Name
,(CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,Amount
--, CAST(ISNULL(Amount/NULLIF(dbo.CalculateNumberOFWorkDays(Start_Date, End_Date),0),0) as money)
,Start_Date
,End_Date
,Probability
,YEAR(c.Date)
,Month(c.Date)
,c.MonthName
,dbo.CalculateNumberOFWorkDays(Start_Date, End_Date)
ORDER BY Office
, (CASE
WHEN recordtypeid = 'LAS1' THEN 'S'
END)
,(CASE WHEN StageName = 'Closed Won' THEN 'Closed Won'
ELSE 'Open'
END)
, [Start_Date], Month(c.Date), AccountName, Row;
I tried adding another left outer join to this and using this a sub query and the join essentially on the calendar based on the year and month, but that did not seem to work either. Suggestions would be extremely appreciated.
--Date Calendar for each location:
;With DateSequence( [Date], Locatio) as
(
Select CAST(#fromdate as DATE) as [Date], oo.Office as location
union all
Select CAST(dateadd(day, 1, [Date]) as Date), oo.Office as location
from DateSequence dts
join Opportunity_offices oo on 1 = 1
where Date < #todate
)
--select result
INSERT INTO CalendarTemp (Location,Date, Day, DayOfWeek, DayOfYear, WeekOfYear, Month, MonthName, Year)
Select
location,
[Date] as [Date],
DATEPART(DAY,[Date]) as [Day],
DATENAME(dw, [Date]) as [DayOfWeek],
DATEPART(DAYOFYEAR,[Date]) as [DayOfYear],
DATEPART(WEEK,[Date]) as [WeekOfYear],
DATEPART(MONTH,[Date]) as [Month],
DATENAME(MONTH,[Date]) as [MonthName],
DATEPART(YEAR,[Date]) as [Year]
from DateSequence option (MaxRecursion 10000)
;
you have your LEFT JOIN backwards if you want all records from CalendarTemp and only those that match from SF_Extracted_Opps then you the CalendarTemp should be the table on the LEFT. You can however switch LEFT JOIN to RIGHT JOIN and it should be fixed. The other issue will be your WHERE statement is using columns from your SF_Extracted_Opps table which will just make that an INNER JOIN again.
here is one way to fix.
SELECT
.....
FROM
CalendarTemp c
LEFT JOIN SF_Extracted_Opps o
ON o.Start_Date <= c.Date AND o.End_Date >= c.Date
AND o.Start_Date <= #todate AND End_Date >= #fromdate
AND o.Office IN (#Location)
AND o.recordtypeid IN ('LAS1')
The other issue you might run into is because you remove weekends from your CalendarTemp Table not all dates are represented I would test with the weekends still in and out and see if you get different results.
this line:
AND o.Start_Date <= #todate AND End_Date >= #fromdate
should not be needed either because you are already limiting the dates from the line before and values in your CalendarTempTable
A note about your CalendarDate table you don't have to go back and delete those records simply add the day of week as a WHERE statement on the select that populates that table.
Edit for All Offices you can use a cross join of your offices table with your CalendarTemp table to do this do it in your final query not the cte that builds the calendar. The problem with doing it in the CTE calendar definition is that it is recursive so you would have to do it in both the anchor and the recursive member definition.
SELECT
.....
FROM
CalendarTemp c
CROSS JOIN Opportunity_offices oo
LEFT JOIN SF_Extracted_Opps o
ON o.Start_Date <= c.Date AND o.End_Date >= c.Date
AND o.Start_Date <= #todate AND End_Date >= #fromdate
AND oo.office = o.Office
AND o.recordtypeid IN ('LAS1')

Can this Full Outer Join query be simplified?

I have bill data from two sources. I want to join them to be able to see where there is a record in one set that is not in the other and vice versa. This query does what I want but I feel like it could be written more elegantly? I only want one field for the account number and bill date (M and Y), and then separate fields for the the charges in each source.
DECLARE #BillDate datetime ='2/1/2016'
SELECT ACCT_NO = COALESCE(BPFACCT,CSTACCT) ,
BillDate = COALESCE(BPFBillDate,CSTBillDate) ,
BPFCharge ,
CSTCharge ,
Delta = ROUND(COALESCE(BPFBill.BPFCharge,0)-COALESCE(CSTBill.CSTCharge,0),2)
FROM
(
SELECT
BPFACCT = acct_no ,
BPFBillDate = cast(billdate as date) ,
BPFCharge = SUM(charge)
FROM
cisbill b
JOIN
cisbilldetail bd ON b.billid=bd.billid
WHERE
billdate>=#BillDate
AND
billdate<DATEADD(MONTH, 1, #BillDate)
GROUP BY
acct_no, billdate
) BPFBill
FULL OUTER JOIN
(
SELECT CSTACCT = acct_no ,
CSTBillDate = cast(bill_date as date) ,
CSTCharge = SUM(new_charges)
FROM
cst_bill
WHERE
bill_date>=#BillDate
AND
bill_date<DATEADD(MONTH, 1, #BillDate)
GROUP BY
acct_no, bill_date
) CSTBill
ON BPFBill.BPFACCT=CSTBill.CSTACCT
AND
BPFBill.BPFBillDate=CSTBill.CSTBillDate
Appreciate any feedback!
I feel like this would give you a more accurate result..
DECLARE #StartDate DATETIME = '2/1/2016',
#EndDate DATETIME
SET #EndDate = DATEADD(MONTH, 1, #BillDate)
SELECT ACCT_NO,
MM,
YY,
BPFCharge = SUM(BPFCharge),
CSTCharge = SUM(CSTCharge)
FROM
(
SELECT acct_no,
MM = MONTH(billdate),
YY = YEAR(billdate),
BPFCharge = charge
CSTCharge = 0.00
FROM cisbill b
JOIN cisbilldetail bd ON b.billid = bd.billid
WHERE billdate >= #StartDate
AND billdate < #EndDate
UNION ALL
SELECT acct_no,
MONTH(bill_date),
YEAR(Bill_date),
0.00,
new_charges
FROM cst_bill
WHERE bill_date >= #StartDate
AND bill_date < #EndDate
) t
GROUP BY ACCT_NO,
MM,
YY

Get records based on Current Date and Configured Data

I am working on automating the SMS sending part in my web application.
SQL Fiddle Link
DurationType table stores whether the sms should be sent out an interval of Hours, Days, Weeks, Months. Referred in SMSConfiguration
CREATE TABLE [dbo].[DurationType](
[Id] [int] NOT NULL PRIMARY KEY,
[DurationType] VARCHAR(10) NOT NULL
)
Bookings table Contains the original Bookings data. For this booking I need to send the SMS based on configuration.
SMS Configuration. Which defines the configuration for sending automatic SMS. It can be send before/after. Before=0, After=1. DurationType can be Hours=1, Days=2, Weeks=3, Months=4
Now I need to find out the list of bookings that needs SMS to be sent out at the current time based on the SMS Configuration Set.
Tried SQL using UNION
DECLARE #currentTime smalldatetime = '2014-07-12 11:15:00'
-- 'SMS CONFIGURED FOR HOURS BASIS'
SELECT B.Id AS BookingId,
B.StartTime AS BookingStartTime,#currentTime As CurrentTime, SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE (DATEDIFF(HOUR, #currentTime, B.StartTime) = SMS.Duration AND SMS.DurationType=1 AND BeforeAfter=0)
OR
(DATEDIFF(HOUR, B.StartTime, #currentTime) = SMS.Duration AND SMS.DurationType=1 AND BeforeAfter=1)
--'SMS CONFIGURED FOR DAYS BASIS'
UNION
SELECT B.Id AS BookingId,
B.StartTime AS BookingStartTime,#currentTime As CurrentTime, SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE (DATEDIFF(DAY, #currentTime, B.StartTime) = SMS.Duration AND SMS.DurationType=2 AND BeforeAfter=0)
OR
(DATEDIFF(DAY, B.StartTime, #currentTime) = SMS.Duration AND SMS.DurationType=2 AND BeforeAfter=1)
--'SMS CONFIGURED FOR WEEKS BASIS'
UNION
SELECT B.Id AS BookingId,
B.StartTime AS BookingStartTime, #currentTime As CurrentTime, SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE (DATEDIFF(DAY, #currentTime, B.StartTime)/7 = SMS.Duration AND SMS.DurationType=3 AND BeforeAfter=0)
OR
(DATEDIFF(DAY, B.StartTime, #currentTime)/7 = SMS.Duration AND SMS.DurationType=3 AND BeforeAfter=1)
--'SMS CONFIGURED FOR MONTHS BASIS'
UNION
SELECT B.Id AS BookingId,
B.StartTime AS BookingStartTime, #currentTime As CurrentTime, SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE (dbo.FullMonthsSeparation(#currentTime, B.StartTime) = SMS.Duration AND SMS.DurationType=4 AND BeforeAfter=0)
OR
(dbo.FullMonthsSeparation(B.StartTime, #currentTime) = SMS.Duration AND SMS.DurationType=4 AND BeforeAfter=1)
Result
Problem:
The SQL procedure will be running every 15 mins. Current query keep returning days/weeks/months records even for the current time '2014-07-12 11:30:00', '2014-07-12 11:45:00', etc
I want a single query that takes care of all Hours/Days/Weeks/Months
calculation and I should be get records only one time when they meet
the correct time. Otherwise I will be sending sms again and again
every 15 mins for day/week/months records matched.
It should consider the following scenarios.
Hour, If booking is 10:15 A.M same day 9:15 A.M if it is before 1 hour configured
Day(24 Hour difference), If booking is 10:15 A.M 3rd day morning 10:15 A.M if it is configured after 3 days in SMSConfiguration
Match Week. If booking is 10:15 A.M today(Wednesday) morning then after 14 days morning 10:15 A.M if it is configured after 2 weeks.
Month also same logic like above.
Try the simplified version FIDDLE , removed Union and used OR conditions
LIST FOR HOURS - RUN Every 15 mins
DECLARE #currentTime smalldatetime = '2014-07-12 11:15:00'
SELECT B.Id AS BookingId, C.Id, C.Name,
B.StartTime AS BookingStartTime,#currentTime As CurrentTime, SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
LEFT JOIN Category C ON C.Id=B.CategoryId
WHERE
(DATEDIFF(MINUTE, #currentTime, B.StartTime) = SMS.Duration*60 AND SMS.DurationType=1 AND BeforeAfter=0)
OR
(DATEDIFF(MINUTE, B.StartTime, #currentTime) = SMS.Duration*60 AND SMS.DurationType=1 AND BeforeAfter=1)
Order BY B.Id
GO
LIST FOR DAYS/WEEKS/MONTHS - RUN Every Day Morning Once
DECLARE #currentTime smalldatetime = '2014-07-12 08:00:00'
SELECT B.Id AS BookingId, C.Id, C.Name,
B.StartTime AS BookingStartTime,#currentTime As CurrentTime, SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
LEFT JOIN Category C ON C.Id=B.CategoryId
WHERE
(((DATEDIFF(DAY, #currentTime, B.StartTime) = SMS.Duration AND SMS.DurationType=2)
OR (DATEDIFF(DAY, #currentTime, B.StartTime) = SMS.Duration*7 AND SMS.DurationType=3)
OR (DATEDIFF(DAY, #currentTime, B.StartTime) = SMS.Duration*30 AND SMS.DurationType=4))
AND BeforeAfter=0)
OR
(((DATEDIFF(DAY, B.StartTime, #currentTime) = SMS.Duration AND SMS.DurationType=2)
OR (DATEDIFF(DAY, #currentTime, B.StartTime) = SMS.Duration*7 AND SMS.DurationType=3)
OR (DATEDIFF(DAY, #currentTime, B.StartTime) = SMS.Duration*30 AND SMS.DurationType=4))
AND BeforeAfter=1)
Order BY B.Id
You seem to have forgotten to pick some columns to return.
DECLARE #currentTime smalldatetime = '2014-07-12 09:15:00';
SELECT [col1], [col2], [etcetera]
FROM
Bookings B
INNER JOIN SMSConfiguration SMS ON SMS.CategoryId=B.CategoryId
WHERE DATEDIFF(hour,B.StartTime,#currentTime)=1
This works
DECLARE #currentTime smalldatetime
set #currentTime= '2014-07-12 09:15:00'
SELECT B.CategoryId FROM
Bookings B
INNER JOIN SMSConfiguration SMS ON SMS.CategoryId=B.CategoryId
WHERE DATEDIFF(hh,B.StartTime,#currentTime)=1
There were two problems in your sql fiddle
1) you didn't set the value of currentTime
2) there were columns in your select statement
Note: I took B.CategoryId as one of the column to fetch records, you can change it as per your requirement
Just swap the dates in DATEDIFF function
Change DATEDIFF(hour, B.StartTime, #currentTime) to DATEDIFF(hour, #currentTime, B.StartTime) - for HOUR only
Because yours one return the value in Negative (-1).
DECLARE #currentTime smalldatetime = '2014-07-12 09:15:00'
SELECT B.Id AS BookingId, #currentTime AS CurrentTime,
B.StartTime AS BookingStartTime, SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE (SMS.CategoryId IS NOT NULL AND
DATEDIFF(HOUR, #currentTime, B.StartTime) = SMS.Duration) OR
(SMS.CategoryId IS NULL AND
(DATEDIFF(DAY, B.StartTime, #currentTime) = SMS.Duration OR
DATEDIFF(MONTH, B.StartTime, #currentTime) = SMS.Duration))
You can use the nested CASE expressions for achieve your results in single query. please try this,
DECLARE #currentTime smalldatetime = '2014-07-12 11:15:00'
SELECT B.Id AS BookingId,
SMS.Duration,
B.StartTime AS BookingStartTime,
#currentTime As CurrentTime,
SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS
ON SMS.CategoryId = B.CategoryId
OR SMS.CategoryId IS NULL
WHERE
SMS.Duration =
CASE
WHEN SMS.DurationType = 1 THEN --'SMS CONFIGURED FOR HOURS BASIS'
CASE WHEN BeforeAfter = 0 THEN
DATEDIFF(HOUR, #currentTime, B.StartTime)
ELSE
DATEDIFF(HOUR, B.StartTime, #currentTime)
END
WHEN SMS.DurationType = 2 THEN --'SMS CONFIGURED FOR DAY BASIS'
CASE WHEN BeforeAfter = 0 THEN
DATEDIFF(DAY, #currentTime, B.StartTime)
ELSE
DATEDIFF(DAY, B.StartTime, #currentTime)
END
WHEN SMS.DurationType = 3 THEN --'SMS CONFIGURED FOR WEEK BASIS'
CASE WHEN BeforeAfter = 0 THEN
DATEDIFF(DAY, #currentTime, B.StartTime)/7
ELSE
DATEDIFF(DAY, B.StartTime, #currentTime)/7
END
ELSE
CASE WHEN BeforeAfter = 0 THEN -- 'SMS CONFIGURED FOR MONTH BASIS'
dbo.FullMonthsSeparation(#currentTime, B.StartTime)
ELSE
dbo.FullMonthsSeparation(B.StartTime, #currentTime)
END
END
ORDER BY BOOKINGID;
you can automate in two schedules one for getting booking details of Hourly sms and other one for Daily/Weekly/Monthly booking details.
Try:
DECLARE #currentTime smalldatetime = '2014-07-12 09:15:00'
SELECT B.Id AS BookingId,
B.StartTime AS BookingStartTime,
#currentTime CURRENT_DT,
SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE ( SMS.DurationType = 1
AND #currentTime = DATEADD(HOUR, (2*SMS.BeforeAfter-1)*SMS.Duration, B.StartTime))
OR
( SMS.DurationType = 2
AND #currentTime = DATEADD(DAY, (2*SMS.BeforeAfter-1)*SMS.Duration, B.StartTime))
OR
( SMS.DurationType = 3
AND #currentTime = DATEADD(WEEK, (2*SMS.BeforeAfter-1)*SMS.Duration, B.StartTime))
OR
( SMS.DurationType = 4
AND #currentTime = DATEADD(MONTH, (2*SMS.BeforeAfter-1)*SMS.Duration, B.StartTime))
The 2*SMS.BeforeAfter-1 converts Before(0) to -1 and After(1) to 1. Thus, adding or subtracting time from the BookingStartTime
[Edit]
The above solution should work with your data model as is. Below I propose simplifying the solution by changing your data model.
Option 1: Change Before and After meta data
http://sqlfiddle.com/#!3/6e9e9/1
Instead of Before = 0 and After = 1, use Before = -1 and After = 1. Then these can be used to change the direction of any date offset
For example:
INSERT INTO [dbo].[SMSConfiguration]
([CategoryId],[SMSText],[BeforeAfter],[Duration],[DurationType],[IsActive])
VALUES
(1,'Before 1 hour',-1,1,1,1)--
INSERT INTO [dbo].[SMSConfiguration]
([CategoryId],[SMSText],[BeforeAfter],[Duration],[DurationType],[IsActive])
VALUES
(NULL,'After 1 hour (no category id))',1,1,1,1)
Updated query:
DECLARE #currentTime smalldatetime = '2014-07-12 09:15:00'
SELECT B.Id AS BookingId,
B.StartTime AS BookingStartTime,
#currentTime CURRENT_DT,
SMS.SMSText
FROM Bookings B INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE ( SMS.DurationType = 1
AND #currentTime = DATEADD(HOUR, SMS.BeforeAfter*SMS.Duration, B.StartTime))
OR
( SMS.DurationType = 2
AND #currentTime = DATEADD(DAY, SMS.BeforeAfter*SMS.Duration, B.StartTime))
OR
( SMS.DurationType = 3
AND #currentTime = DATEADD(WEEK, SMS.BeforeAfter*SMS.Duration, B.StartTime))
OR
( SMS.DurationType = 4
AND #currentTime = DATEADD(MONTH, SMS.BeforeAfter*SMS.Duration, B.StartTime))
Option 2: Allow 30-days = 1 month
http://sqlfiddle.com/#!3/808cd/1
On top of the Before/After change, if a 30 day period is close enough to represent a month, then all duration types could be converted to hours. The question is how particular your customers will be in regard to precision. If you are off by a day or two on a "3 month before" rule, I don't think you will have many complaints.
So your DurationType table could look like this
CREATE TABLE [dbo].[DurationType](
[Id] [int] NOT NULL PRIMARY KEY,
[DurationType] VARCHAR(10) NOT NULL,
[HourConversion] [int] NOT NULL
)
GO
INSERT INTO [dbo].[DurationType]([Id],[DurationType],[HourConversion])
VALUES(1,'Hours',1)
INSERT INTO [dbo].[DurationType]([Id],[DurationType],[HourConversion])
VALUES(2,'Days',24)
INSERT INTO [dbo].[DurationType]([Id],[DurationType],[HourConversion])
VALUES(3,'Weeks',24*7)
INSERT INTO [dbo].[DurationType]([Id],[DurationType],[HourConversion])
VALUES(4,'Months',24*30)
With these changes, the query could be modified to:
DECLARE #currentTime smalldatetime = '2014-07-12 09:15:00'
SELECT B.Id AS BookingId,
B.StartTime AS BookingStartTime,
#currentTime CURRENT_DT,
SMS.SMSText
FROM Bookings B
INNER JOIN
SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
INNER JOIN
DurationType DT ON DT.Id = SMS.DurationType
WHERE #currentTime = DATEADD(HOUR, SMS.BeforeAfter*SMS.Duration*DT.HourConversion, B.StartTime)

How to output only one max value from this query in SQL?

Yesterday Thomas helped me a lot by providing exactly the query I wanted. And now I need a variant of it, and hopes someone can help me out.
I want it to output only one row, namely a max value - but it has to build on the algorithm in the following query:
WITH Calendar AS (SELECT CAST(#StartDate AS datetime) AS Date
UNION ALL
SELECT DATEADD(d, 1, Date) AS Expr1
FROM Calendar AS Calendar_1
WHERE (DATEADD(d, 1, Date) < #EndDate))
SELECT C.Date, C2.Country, COALESCE (SUM(R.[Amount of people per day needed]), 0) AS [Allocated testers]
FROM Calendar AS C CROSS JOIN
Country AS C2 LEFT OUTER JOIN
Requests AS R ON C.Date BETWEEN R.[Start date] AND R.[End date] AND R.CountryID = C2.CountryID
WHERE (C2.Country = #Country)
GROUP BY C.Date, C2.Country OPTION (MAXRECURSION 0)
The output from above will be like:
Date Country Allocated testers
06/01/2010 Chile 3
06/02/2010 Chile 4
06/03/2010 Chile 0
06/04/2010 Chile 0
06/05/2010 Chile 19
but what I need right now is
Allocated testers
19
that is - only one column - one row - the max value itself... (for the (via parameters (that already exists)) selected period of dates and country)
use order and limit
ORDER BY 'people needed DESC' LIMIT 1
EDITED
as LIMIT is not exist in sql
use ORDER BY and TOP
select TOP 1 .... ORDER BY 'people needed' DESC
WITH Calendar
AS (
SELECT
CAST(#StartDate AS datetime) AS Date
UNION ALL
SELECT
DATEADD(d, 1, Date) AS Expr1
FROM
Calendar AS Calendar_1
WHERE
( DATEADD(d, 1, Date) < #EndDate )
)
SELECT TOP 1 *
FROM
(
SELECT
C.Date
,C2.Country
,COALESCE(SUM(R.[Amount of people per day needed]), 0) AS [Allocated testers]
FROM
Calendar AS C
CROSS JOIN Country AS C2
LEFT OUTER JOIN Requests AS R
ON C.Date BETWEEN R.[Start date] AND R.[End date]
AND R.CountryID = C2.CountryID
WHERE
( C2.Country = #Country )
GROUP BY
C.Date
,C2.Country
OPTION
( MAXRECURSION 0 )
) lst
ORDER BY lst.[Allocated testers] DESC
Full example following the discussion in #Salil answer..
WITH Calendar AS (SELECT CAST(#StartDate AS datetime) AS Date
UNION ALL
SELECT DATEADD(d, 1, Date) AS Expr1
FROM Calendar AS Calendar_1
WHERE (DATEADD(d, 1, Date) < #EndDate))
SELECT TOP 1 C.Date, C2.Country, COALESCE (SUM(R.[Amount of people per day needed]), 0) AS [Allocated testers]
FROM Calendar AS C CROSS JOIN
Country AS C2 LEFT OUTER JOIN
Requests AS R ON C.Date BETWEEN R.[Start date] AND R.[End date] AND R.CountryID = C2.CountryID
WHERE (C2.Country = #Country)
GROUP BY C.Date, C2.Country
ORDER BY 3 DESC
OPTION (MAXRECURSION 0)
the ORDER BY 3 means order by the 3rd field in the SELECT statement.. so if you remove the first two fields, change this accordingly..