daily incremental results on table where transaction date <= #Date parameter - sql

specifically looking for General Leger results. This means that I can't sum up transactions for specfic dates, or cant run Between date.
to get the results for say, today I would need to query the table for all transactions <= #Today.
That said, i am tasked with running this for every single day in 2020 thus far. is there a method to do this where i dont have to manually run for each day myself?
Query example:
glo.GLValue
, Sum(UnitCR) AS 'Credit'
, Sum(UnitDR) AS 'Debit'
, sum(FirmCR) AS 'FirmCredit'
, sum(FirmDR) AS 'FirmDebit'
FROM glacct ga
inner join gldetail gd on gd.glacct = ga.AcctIndex
inner join glnatural gn on ga.glnatural = gn.GLNaturalID
inner join glunit glu on ga.glunit = glu.GLUnitID
inner join gloffice glo on ga.GLOffice = glo.GLOfficeID
WHERE gn.GLNat IN ('11001','11002','11003','11005','11007','11011','11016','11019','11020','11021','11022','11024','11025','11026','11027','11032','11033',
'11034','11035','11036','11037','11040','11041','11042','11043','11044','11050','11051','11052','11053','11190','11199','11201','11202','11203','11204',
'11205','11206','11207','11301','11603','11700','11705','11801','11802','11803','11804','11806','11807','11808','11809','11901')--,'22001')
AND gd.PostDate <= #Yesterday
GROUP BY
glo.GLValue

Create a sub-table that give the sums for each PostDate and GLValue similar to above but also grouped on PostDate, then join that to your select above, e.g.
inner join gloffice glo on ga.GLOffice = glo.GLOfficeID
inner join ( ... new grouped select here ...) gs on gs.GLValue = glo.GlValue and gs.PostDate < gd.PostDate
Now you should be able to sum the gs values:
, Sum(gs.Credit) as Credit
, Sum(gs.Debit) as Debit
etc.

Related

Filter with Dates but keep the calculation

I would like to know what can you do in the following scenario:
Lets say I am filtering on a date in the where clause (between eomonth(#StartDate) and eomonth(getdate()-1). I have a calculated column that is correct when I run the query without any filter, but the problem is that when I filter lets say #StartDate = 06/30/2017 then the calculations will obviously change. Is there any way of doing this?
The calculated column is a windowing function.
Edited:
I have added a picture of the data. So I am using a windowing function to calculate the agentfourmonthperiod. You will see that it sums the units for that month and the 3 previous months. This I dont want to change when filtering. So the units and agentfourmonthperiod columns should stay exactly the same after filtering on the #StartDate. Please see data below:
I want to design an SSRS report that the user will be filtering on the #StartDate, but then it should show the calculation as it does when no filter in used.
Hope this makes sense. Otherwise I can add the code. Its just quite long though.
Code:
WITH DATA
AS
(
SELECT
EOMONTH(SubmissionDates.original_date_c) AS IntakeMonth,
ProvincialArea.SAD_ProvMananger AS ProvManager,
RegionalArea.SAD_RegMananger AS RegManager,
SalesArea.SAD_SalesManager AS AreaSalesManager,
ConsultantUserExt.name AS Consultant,
COUNT(LeadsLink.LeadsID) OVER(PARTITION BY ConsultantUserExt.name, EOMONTH(SubmissionDates.original_date_c,0) ORDER BY EOMONTH(SubmissionDates.original_date_c,0)) AS Unit,
ROW_NUMBER() OVER(PARTITION BY ConsultantUserExt.name, EOMONTH(SubmissionDates.original_date_c,0) ORDER BY EOMONTH(SubmissionDates.original_date_c,0)) AS rn
FROM Import.OobaApplication as Application
LEFT OUTER JOIN Import.OobaApplicant applicant ON application.ApplicationID = applicant.ApplicationID
AND applicant.PrincipleApplication = 'Y'
LEFT OUTER JOIN usr_userext_cstm ON Application.Consultant = usr_userext_cstm.comcorp_key_c
or Application.Consultant = usr_userext_cstm.deal_maker_key_c
or Application.Consultant = usr_userext_cstm.ops_key_c
LEFT OUTER JOIN usr_userext AS ConsultantUserExt ON usr_userext_cstm.id_c = ConsultantUserExt.id AND ConsultantUserExt.deleted = 0
LEFT OUTER JOIN usr_userext_cstm AS ConsultantUserExtCstm on ConsultantUserExt.id = ConsultantUserExtCstm.id_c
LEFT OUTER JOIN CapcubedInternalDB.dbo.ProvincialArea AS ProvincialArea ON ConsultantUserExtCstm.sad_provincialmanager_c = ProvincialArea.ID
LEFT OUTER JOIN CapcubedInternalDB.dbo.RegionalArea AS RegionalArea ON ConsultantUserExtCstm.sad_regionalmanager_c = RegionalArea.ID
LEFT OUTER JOIN CapcubedInternalDB.dbo.SalesArea AS SalesArea ON ConsultantUserExtCstm.sad_salesmanager_c = SalesArea.ID
LEFT OUTER JOIN CapcubedInternalDB.dbo.LeadsLink AS LeadsLink ON Application.ApplicationID = LeadsLink.GroupCode
LEFT OUTER JOIN suitecrmprod.dbo.leads AS SuiteLeads ON LeadsLink.LeadsID = SuiteLeads.ID
--Latest Bank Submission
LEFT OUTER JOIN (SELECT
bankSub.ApplicationID As BankSubAppID, bankSub.SubmissionDate,
bankSub.Bank, bankSub.RequiredLoanAmount,
bankSub.BankCode AS BankSubBankCode
FROM Import.OobaBankSubmission bankSub
LEFT OUTER JOIN Import.OobaBankSubmission later ON bankSub.ApplicationID = later.ApplicationID
AND bankSub.SubmissionDate > later.SubmissionDate
WHERE later.applicationID IS NULL) AS BankSub ON Application.ApplicationID = BankSub.BankSubAppID
LEFT OUTER JOIN ccrep_calendar_cstm AS SubmissionDates ON CONVERT(VARCHAR(10),BankSub.SubmissionDate,101) = SubmissionDates.original_date_c
WHERE SubmissionDates.cc_date_c BETWEEN COALESCE(EOMONTH(#StartDate), '01/31/2016') AND COALESCE(#EndDate, GETDATE(), -1)
AND ConsultantUserExtCstm.consultantstatus_c NOT LIKE 2
)
SELECT *
INTO #Rn
FROM DATA
WHERE rn = 1
SELECT i.IntakeMonth, c.ProvManager, c.RegManager, c.AreaSalesManager, c.Consultant, COALESCE(#Rn.Unit, 0) AS Unit
INTO #FillData
FROM (SELECT DISTINCT IntakeMonth FROM #Rn) AS i
CROSS JOIN
(SELECT DISTINCT Consultant, ProvManager, RegManager, AreaSalesManager FROM #Rn) AS c
LEFT OUTER JOIN #Rn ON #Rn.IntakeMonth = i.IntakeMonth AND #Rn.Consultant = c.Consultant
ORDER BY Consultant, IntakeMonth
SELECT
IntakeMonth,
Consultant,
Unit,
SUM(Unit) OVER(PARTITION BY Consultant ORDER BY Consultant ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS agentfourmonthperiod
FROM #FillData
WHERE ('(All)' IN (#ProvincialManager) OR (ProvManager IN (#ProvincialManager)))
AND ('(All)' IN (#RegionalManager) OR (RegManager IN (#RegionalManager)))
AND ('(All)' IN (#AreaSalesManager) OR (AreaSalesManager IN (#AreaSalesManager)))
AND ('(All)' IN (#Consultant) OR (Consultant IN (#Consultant)))
DROP TABLE #Rn
DROP TABLE #FillData
You could of course remove any filter on dates from the query and apply them directly in the tablix of your report. Obvously, this means that SQL Server has to return all the data each time the report is run, so I guess that this isn't what you want.
For the window function to have access to the previous 3 rows, you will have to include the previous 3 months in your calculation. To achieve this, change the first condition in the WHERE clause in the cte (data) to something like this:
SubmissionDates.cc_date_c
BETWEEN
ISNULL(DATEADD(month, DATEDIFF(month, 0, #StartDate)-3, 0), '01/10/2015')
AND
ISNULL(#EndDate, DATEADD(day, DATEDIFF(day, 0, GETDATE())-1, 0))
As I thought that your date filter logic was wrong, I changed it to include the dates from the beginning of the month rather than from the end.
Now that the previous 3 months are included, we can apply a filter in the end to exclude the previous months from display, but this has to be done after using the window function, for example with another cte:
WITH calc AS (
SELECT
IntakeMonth,
Consultant,
Unit,
SUM(Unit) OVER(PARTITION BY Consultant ORDER BY Consultant ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS agentfourmonthperiod
FROM #FillData
WHERE ('(All)' IN (#ProvincialManager) OR (ProvManager IN (#ProvincialManager)))
AND ('(All)' IN (#RegionalManager) OR (RegManager IN (#RegionalManager)))
AND ('(All)' IN (#AreaSalesManager) OR (AreaSalesManager IN (#AreaSalesManager)))
AND ('(All)' IN (#Consultant) OR (Consultant IN (#Consultant)))
)
SELECT IntakeMonth, Consultant, Unit, agentfourmonthperiod
FROM calc
WHERE IntakeMonth >= ISNULL(EOMONTH(#StartDate), '01/31/2016')

How to fix "Conversion from string "August" to type 'Date' is not vaid in SSRS

SELECT
a.ItemCode,
SUM(a.NoOfApplication) AS NoOfApplication,
SUM(a.NoOfAccomplished) AS NoOfAccomplished,
SUM(a.NoOfPending) AS NoOfPending,
SUM(a.NoOfDocumentCompliance) AS NoOfDocumentCompliance,
a.[Year]
FROM
(SELECT
ItemCode,
COUNT(am.ReferenceNumber) AS NoOfApplication,
COUNT(TNA.NoOfAccomplished) AS NoOfAccomplished,
COUNT(TNP.NoOfPending) AS NoOfPending,
SUM(FDC.NoOfDocumentCompliance) AS NoOfDocumentCompliance,
DATENAME(month, ad.applicationdate) AS [Year]
FROM
AppTypes at
INNER JOIN
AssessmentMainDetails am ON at.Category = am.Category
INNER JOIN
InspectionProcesses i ON am.ReferenceNumber = i.ReferenceNo
LEFT JOIN
(SELECT
COUNT(Status) AS NoOfDocumentCompliance,
ReferenceNumber, Status
FROM
ApplicationStatus
WHERE
Status = 'For Document Compliance'
GROUP BY
ReferenceNumber, Status) AS FDC ON FDC.ReferenceNumber = i.ReferenceNo
LEFT JOIN
(SELECT
COUNT(ReferenceNo) AS NoOfAccomplished,
ReferenceNo
FROM
InspectionProcesses
WHERE
DateOfInspection <> ''
GROUP BY
ReferenceNo) AS TNA ON TNA.ReferenceNo = i.ReferenceNo
LEFT JOIN
(SELECT
COUNT(ReferenceNo) AS NoOfPending, ReferenceNo
FROM
InspectionProcesses
WHERE
DateOfInspection = ''
GROUP BY
ReferenceNo) AS TNP ON TNP.ReferenceNo = i.ReferenceNo
INNER JOIN
ApplicationDetails ad on i.ReferenceNo = ad.ReferenceNumber
INNER JOIN
Companies c on ad.CompanyId = c.CompanyID
INNER JOIN
Zones z on c.zonecode = z.zonecode
INNER JOIN
ZoneGroups zg on z.ZoneGroup = zg.ZoneGroupId
WHERE
DateOfInspection = ''
AND ad.ApplicationDate BETWEEN '2017-08-01' AND '2017-09-30'
AND zg.ZoneGroupCode = 'HO'
AND z.ZoneCode = 'VIDC'
GROUP BY
ItemCode, DATENAME(month, ad.applicationdate)) a
GROUP BY
a.ItemCode, a.[Year]
This my code, I already converted my date to get the month name. Please I need help
Look carefully. That giant derived table (a - nice meaningful name btw) has the same group by clause as the outermost query. So that means that [a] has a single row per ItemCode and datename(month, ad.applicationdate). Therefore, there is nothing to sum in your outer query since it is grouping by the same columns.
You also have the expression:
DateOfInspection = ''
which is highly suspicious based on the name of the column. What datatype is the DateOfInspection column? Doesn't sound like it should be string-based.
And lastly, the error message you posted sounds like it comes from SSRS and not sql server. Is that the case? Does your query run correctly from SSMS? Then the problem is in your report - and it would seem that you attempt to manipulate or interpret the Year column as a date (perhaps for sorting?). It also seems a bit short-sighted in your report design that your "Year" column is actually the name of a month and that your resultset does not include the year in some fashion. What happens when your data spans more than twelve months? And how do you intend to sort your report when you have month name but not month number?

Show 0 for null rows

I'm working on a comparison query that will compare what was processed for the different credit card types versus what was actually processed by our POS service. I have ran into an issue where I am not seeing rows for certain card types if they did not process any payments on a given day but may have given a refund. IE: Nothing purchased with VISA but a VISA refund was given, but due to no purchases the row does not show up even though there's a refund for that type.
Here is my query:
WITH tran_total AS (
SELECT CONCAT(c.id_str, ' - ', c.clinic_str) AS Clinics,
t.clinic,
c.xcharge_mid,
p.pay_desc,
CAST(t.time_ran AS date) AS tran_date,
CASE WHEN SUM(t.amount) <> 0 THEN SUM(t.amount) * - 1
ELSE 0.00 END AS collection
FROM dbo.transactions AS t INNER JOIN
dbo.clinic_master AS c ON t.clinic = c.clinic INNER JOIN
dbo.paytype AS p ON t.clinic = p.clinic AND t.paytype_id = p.paytype_id
WHERE (t.time_ran > GETDATE() - 10)
AND (t.paytype_id IS NOT NULL)
AND (p.pay_desc = 'Visa' OR
p.pay_desc = 'MasterCard' OR
p.pay_desc = 'American Express' OR
p.pay_desc = 'Discover')
GROUP BY c.id_str, c.clinic_str, t.clinic, c.xcharge_mid, p.pay_desc, CAST(t.time_ran AS date))
SELECT w.Clinics,
w.pay_desc,
w.tran_date,
w.collection,
CASE WHEN w.pay_desc = 'Visa' THEN (SUM(VI_pur) - SUM(VI_ref))
WHEN w.pay_desc = 'MasterCard' THEN (SUM(MC_pur) - SUM(MC_ref))
WHEN w.pay_desc = 'American Express' THEN (SUM(AX_pur) - SUM(AX_ref))
WHEN w.pay_desc = 'Discover' THEN (SUM(DI_pur) - SUM(DI_ref)) END AS xcharge
FROM tran_total AS w LEFT OUTER JOIN
dbo.xcharge AS x ON w.xcharge_mid = x.xcharge_mid AND w.tran_date = x.settle_date
GROUP BY w.Clinics, w.pay_desc, w.tran_date, w.collection
ORDER BY w.Clinics, w.tran_date
I've thought about switching this around and starting with the comparison from the xcharge table but there is not a pay_desc that links those easily and if something is processed as the wrong card (VISA is chosen but Discover is scanned) then I feel the rows would be missed still.
What I would like, is for all of the pay_desc to show up for each tran_date even if the SUM(t.amount) does not have a value. I've tried a case statement to accomplish this without any luck.
EDIT: I feel the issue is more due to the t.time_ran variable. If there isn't a payment for a given pay_desc then there won't be a t.time_ran either, is there a function I can do to list out dates between GETDATE()-10 AND GETDATE() kind of thing and just have the t.time_ran and x.settle_date match up to that variable instead?
Any thoughts on this? Thanks in advance
UPDATE:
I have created a calendar table with individual dates and have tried the following query:
WITH tran_test AS(
SELECT cal.calendardate AS cd,
p.clinic,
p.pay_desc,
p.paytype_id
FROM calendar cal, paytype p
WHERE cal.calendardate BETWEEN GETDATE()-10 AND GETDATE()+1
AND (p.pay_desc = 'Visa' OR
p.pay_desc = 'MasterCard' OR
p.pay_desc = 'American Express' OR
p.pay_desc = 'Discover'))
SELECT w.cd,
CONCAT(c.id_str, ' - ', c.clinic_str) AS Clinics,
w.pay_desc,
ISNULL(SUM(t.amount)*-1, 0) AS collection,
CASE WHEN w.pay_desc = 'Visa' THEN (SUM(x.VI_pur) - SUM(x.VI_ref))
WHEN w.pay_desc = 'MasterCard' THEN (SUM(x.MC_pur) - SUM(x.MC_ref))
WHEN w.pay_desc = 'American Express' THEN (SUM(x.AX_pur) - SUM(x.AX_ref))
WHEN w.pay_desc = 'Discover' THEN (SUM(x.DI_pur) - SUM(x.DI_ref)) END AS xcharge
FROM tran_test w
INNER JOIN clinic_master c
ON (w.clinic=c.clinic)
LEFT OUTER JOIN transactions t
ON (t.clinic=w.clinic AND t.paytype_id=w.paytype_id AND CAST(t.time_ran AS date) = w.cd)
LEFT OUTER JOIN xcharge x
ON (w.cd=x.settle_date AND c.xcharge_mid=x.xcharge_mid)
GROUP BY w.cd, c.id_str, c.clinic_str, w.pay_desc
ORDER BY w.cd
However, I'm not getting an issue where my xcharge column seems to multiplying itself by random intervals and I'm not sure why.
It sounds like what you need is a Calendar table. You can generate one as part of a CTE (many examples on this site or just search Google), but IMO you should have a persistent table in your database. This way you can mark certain days as bank holidays, what quarter your business considers them to be in, etc.
Once you have that table in place you can SELECT your dates from that table and then LEFT OUTER JOIN to that table from your Transactions table. Any dates without matching rows in Transactions will still show up with a row, but you'll have 0.00 value for your SUM.

SQL - Joining a list of dates with observations within a date range

I have a sample query below that uses GETDATE to pull the most recent estimates. The table has TWO date columns, effectiveDate and toDate. The problem? I want to pull a weekly value so I can have a time series of estimates. If I run the query now, I will end up with all the estimates as of today, but I also want to know what they were last week, the week before, etc.
Should I create a new table containing the dates that I want and then join them against the results of the query. This is where I am stuck. Thank you.
select GETDATE() as observeDate
, (select C.companyName from ciqCompany C where C.companyId = EP.companyId) as companyName
, (select EPT.periodTypeName from ciqEstimatePeriodType EPT where EPT.periodTypeId = EP.periodTypeId) as periodTypeName
, EP.fiscalYear
, EB.brokerName as brokerName
, EA.firstName+' '+EA.lastName as AnalystName
, EDND.tradingitemid
, (select DI.dataItemName from ciqdataitem DI where DI.dataitemid = EDND.dataitemid) as dataItemName
, (select EAS.accountingStandardDescription from dbo.ciqEstimateAccountingStd EAS where EAS.accountingStandardId = EDND.accountingStandardId) as AccountingStandard
, (select Cu.ISOCode from ciqCurrency Cu where Cu.currencyid = EDND.currencyid) as ISOCode
, (select EST.estimateScaleName from ciqEstimateScaleType EST where EST.estimateScaleId = EDND.estimateScaleId) as estimateScaleName
,EDND.dataItemValue,EDND.effectiveDate,EDND.isExcluded
from ciqEstimatePeriod EP
--- link estimate period table to detailed numeric data table
----------------------------------------------------------
join ciqEstimateDetailNumericData EDND
on EDND.estimatePeriodId = EP.estimatePeriodId
and GETDATE() between EDND.effectiveDate and EDND.toDate
----------------------------------------------------------
left outer join ciqEstimateBroker EB
on EB.estimateBrokerId = EDND.estimateBrokerId --- left outer join must be used if you receive any of the anonymous estimates packages
left outer join ciqEstimateAnalyst EA
on EA.estimateAnalystId = EDND.estimateAnalystId --- left outer join must be used if you receive any of the anonymous estimates packages
where EP.companyId = 112350 -- IBM
and EP.periodTypeId = 1 -- annual
and EDND.dataItemId = 21634 --- EPS Normalized (Detailed)
and EP.fiscalYear = 2010
order by 4,5,6,10
This query is complicated enough as it is - I would hate to add to it. I would probably turn it into a view or a stored proc and query from it as needed. Then you could also have, instead of just GETDATE(), a date range as input.

Compare values from one table with the results from a query?

First, I will explain the what is being captured. User's have a member level associated with their accounts (Bronze, Gold, Diamond, etc). A nightly job needs to run to calculate the orders from today a year back. If the order total for a given user goes over or under a certain amount their level is upgraded or downgraded. The table where the level information is stored will not change much, but the minimum and maximum amount thresholds may over time. This is what the table looks like:
CREATE TABLE [dbo].[MemberAdvantageLevels] (
[Id] int NOT NULL IDENTITY(1,1) ,
[Name] varchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[MinAmount] int NOT NULL ,
[MaxAmount] int NOT NULL ,
CONSTRAINT [PK__MemberAd__3214EC070D9DF1C7] PRIMARY KEY ([Id])
)
ON [PRIMARY]
GO
I wrote a query that will group the orders by user for the year to date. The query includes their current member level.
SELECT
Sum(dbo.tbh_Orders.SubTotal) AS OrderTotals,
Count(dbo.UserProfile.UserId) AS UserOrders,
dbo.UserProfile.UserId,
dbo.UserProfile.UserName,
dbo.UserProfile.Email,
dbo.MemberAdvantageLevels.Name,
dbo.MemberAdvantageLevels.MinAmount,
dbo.MemberAdvantageLevels.MaxAmount,
dbo.UserMemberAdvantageLevels.LevelAchievmentDate,
dbo.UserMemberAdvantageLevels.LevelAchiementAmount,
dbo.UserMemberAdvantageLevels.IsCurrent as IsCurrentLevel,
dbo.MemberAdvantageLevels.Id as MemberLevelId,
FROM
dbo.tbh_Orders
INNER JOIN dbo.tbh_OrderStatuses ON dbo.tbh_Orders.StatusID = dbo.tbh_OrderStatuses.OrderStatusID
INNER JOIN dbo.UserProfile ON dbo.tbh_Orders.CustomerID = dbo.UserProfile.UserId
INNER JOIN dbo.UserMemberAdvantageLevels ON dbo.UserProfile.UserId = dbo.UserMemberAdvantageLevels.UserId
INNER JOIN dbo.MemberAdvantageLevels ON dbo.UserMemberAdvantageLevels.MemberAdvantageLevelId = dbo.MemberAdvantageLevels.Id
WHERE
dbo.tbh_OrderStatuses.OrderStatusID = 4 AND
(dbo.tbh_Orders.AddedDate BETWEEN dateadd(year,-1,getdate()) AND GETDATE()) and IsCurrent = 1
GROUP BY
dbo.UserProfile.UserId,
dbo.UserProfile.UserName,
dbo.UserProfile.Email,
dbo.MemberAdvantageLevels.Name,
dbo.MemberAdvantageLevels.MinAmount,
dbo.MemberAdvantageLevels.MaxAmount,
dbo.UserMemberAdvantageLevels.LevelAchievmentDate,
dbo.UserMemberAdvantageLevels.LevelAchiementAmount,
dbo.UserMemberAdvantageLevels.IsCurrent,
dbo.MemberAdvantageLevels.Id
So, I need to check the OrdersTotal and if it exceeds the current level threshold, I then need to find the Level that fits their current order total and create a new record with their new level.
So for example, lets say jon#doe.com currently is at bronze. The MinAmount for bronze is 0 and the MaxAmount is 999. Currently his Orders for the year are at $2500. I need to find the level that $2500 fits within and upgrade his account. I also need to check their LevelAchievmentDate and if it is outside of the current year we may need to demote the user if there has been no activity.
I was thinking I could create a temp table that holds the results of all levels and then somehow create a CASE statement in the query above to determine the new level. I don't know if that is possible. Or, is it better to iterate over my order results and perform additional queries? If I use the iteration pattern I know i can use the When statement to iterate over the rows.
Update
I updated my Query A bit and so far came up with this, but I may need more information than just the ID from the SubQuery
Select * into #memLevels from MemberAdvantageLevels
SELECT
Sum(dbo.tbh_Orders.SubTotal) AS OrderTotals,
Count(dbo.AZProfile.UserId) AS UserOrders,
dbo.AZProfile.UserId,
dbo.AZProfile.UserName,
dbo.AZProfile.Email,
dbo.MemberAdvantageLevels.Name,
dbo.MemberAdvantageLevels.MinAmount,
dbo.MemberAdvantageLevels.MaxAmount,
dbo.UserMemberAdvantageLevels.LevelAchievmentDate,
dbo.UserMemberAdvantageLevels.LevelAchiementAmount,
dbo.UserMemberAdvantageLevels.IsCurrent as IsCurrentLevel,
dbo.MemberAdvantageLevels.Id as MemberLevelId,
(Select Id from #memLevels where Sum(dbo.tbh_Orders.SubTotal) >= #memLevels.MinAmount and Sum(dbo.tbh_Orders.SubTotal) <= #memLevels.MaxAmount) as NewLevelId
FROM
dbo.tbh_Orders
INNER JOIN dbo.tbh_OrderStatuses ON dbo.tbh_Orders.StatusID = dbo.tbh_OrderStatuses.OrderStatusID
INNER JOIN dbo.AZProfile ON dbo.tbh_Orders.CustomerID = dbo.AZProfile.UserId
INNER JOIN dbo.UserMemberAdvantageLevels ON dbo.AZProfile.UserId = dbo.UserMemberAdvantageLevels.UserId
INNER JOIN dbo.MemberAdvantageLevels ON dbo.UserMemberAdvantageLevels.MemberAdvantageLevelId = dbo.MemberAdvantageLevels.Id
WHERE
dbo.tbh_OrderStatuses.OrderStatusID = 4 AND
(dbo.tbh_Orders.AddedDate BETWEEN dateadd(year,-1,getdate()) AND GETDATE()) and IsCurrent = 1
GROUP BY
dbo.AZProfile.UserId,
dbo.AZProfile.UserName,
dbo.AzProfile.Email,
dbo.MemberAdvantageLevels.Name,
dbo.MemberAdvantageLevels.MinAmount,
dbo.MemberAdvantageLevels.MaxAmount,
dbo.UserMemberAdvantageLevels.LevelAchievmentDate,
dbo.UserMemberAdvantageLevels.LevelAchiementAmount,
dbo.UserMemberAdvantageLevels.IsCurrent,
dbo.MemberAdvantageLevels.Id
This hasn't been syntax checked or tested but should handle the inserts and updates you describe. The insert can be done as single statement using a derived/virtual table which contains the orders group by caluclation. Note that both the insert and update statement be done within the same transaction to ensure no two records for the same user can end up with IsCurrent = 1
INSERT UserMemberAdvantageLevels (UserId, MemberAdvantageLevelId, IsCurrent,
LevelAchiementAmount, LevelAchievmentDate)
SELECT t.UserId, mal.Id, 1, t.OrderTotals, GETDATE()
FROM
(SELECT ulp.UserId, SUM(ord.SubTotal) OrderTotals, COUNT(ulp.UserId) UserOrders
FROM UserLevelProfile ulp
INNER JOIN tbh_Orders ord ON (ord.CustomerId = ulp.UserId)
WHERE ord.StatusID = 4
AND ord.AddedDate BETWEEN DATEADD(year,-1,GETDATE()) AND GETDATE()
GROUP BY ulp.UserId) AS t
INNER JOIN MemberAdvantageLevels mal
ON (t.OrderTotals BETWEEN mal.MinAmount AND mal.MaxAmount)
-- Left join needed on next line in case user doesn't currently have a level
LEFT JOIN UserMemberAdvantageLevels umal ON (umal.UserId = t.UserId)
WHERE umal.MemberAdvantageLevelId IS NULL -- First time user has been awarded a level
OR (mal.Id <> umal.MemberAdvantageLevelId -- Level has changed
AND (t.OrderTotals > umal.LevelAchiementAmount -- Acheivement has increased (promotion)
OR t.UserOrders = 0)) -- No. of orders placed is zero (de-motion)
/* Reset IsCurrent flag where new record has been added */
UPDATE UserMemberAdvantageLevels
SET umal1.IsCurrent=0
FROM UserMemberAdvantageLevels umal1
INNER JOIN UserMemberAdvantageLevels umal2 On (umal2.UserId = umal1.UserId)
WHERE umal1.IsCurrent = 1
AND umal2.IsCurrent = 2
AND umal1.LevelAchievmentDate < umal2.LevelAchievmentDate)
One approach:
with cte as
(SELECT Sum(o.SubTotal) AS OrderTotals,
Count(p.UserId) AS UserOrders,
p.UserId,
p.UserName,
p.Email,
l.Name,
l.MinAmount,
l.MaxAmount,
ul.LevelAchievmentDate,
ul.LevelAchiementAmount,
ul.IsCurrent as IsCurrentLevel,
l.Id as MemberLevelId
FROM dbo.tbh_Orders o
INNER JOIN dbo.UserProfile p ON o.CustomerID = p.UserId
INNER JOIN dbo.UserMemberAdvantageLevels ul ON p.UserId = ul.UserId
INNER JOIN dbo.MemberAdvantageLevels l ON ul.MemberAdvantageLevelId = l.Id
WHERE o.StatusID = 4 AND
o.AddedDate BETWEEN dateadd(year,-1,getdate()) AND GETDATE() and
IsCurrent = 1
GROUP BY
p.UserId, p.UserName, p.Email, l.Name, l.MinAmount, l.MaxAmount,
ul.LevelAchievmentDate, ul.LevelAchiementAmount, ul.IsCurrent, l.Id)
select cte.*, ml.*
from cte
join #memLevels ml
on cte.OrderTotals >= ml.MinAmount and cte.OrderTotals <= ml.MaxAmount