How to calculate prior year sales data in SQL - sql

I'm attempting to build a table summarizing sales data by week. In it, I'm trying to have one of the adjacent columns show the sales figures for the same fiscal week during the prior year (which due to my organizations fiscal calendar, had a 53rd week last year). I also have need to compare (Comp Units/Comp Sales) to a period 52 weeks ago which is an entirely different fiscal week (Think Week 9 of 2019 comparing to Week 10 2018).
I've tried using both unions and full outer joins, but given the way the way my data is, they're inefficient (Because this is weekly data, unions ended up being inefficient as I needed to leave the date information out of the initial query, then updating columns in my table to reflect the week the data is for. This is obviously rife with opportunity for error, but also time consuming to do 105 times), or just didn't work (attempting a full outer join was returning the wrong answers for all columns). I've also tried utilizing CTEs as well, and that's not working for me either. I'm currently trying a CASE Statement, but that's also returning a null value. I'm not quite sure where to go next
#STANDARDSQL
SELECT
DTL.SKU_NBR AS SKU_NBR
, SLS.STR_NBR AS STR_NBR
, CONCAT(TRIM(CAST(SKU_HIER.SKU_NBR AS STRING)), ' ', '-', ' ', TRIM(SKU_HIER.SKU_DESC)) AS SKU
, CONCAT(TRIM(CAST(SKU_HIER.EXT_SUB_CLASS_NBR AS STRING)), ' ', '-', ' ', TRIM(SKU_HIER.SUB_CLASS_DESC)) AS SUB_CLASS
, CONCAT(TRIM(CAST(SKU_HIER.EXT_SUB_SC_NBR AS STRING)), ' ', '-', ' ', TRIM(SKU_HIER.SUB_SC_DESC)) AS SUB_SUB_CLASS
, LOCATION.MKT_NM AS MARKET_NAME
, LOCATION.RGN_NM AS REGION_NAME
, LOCATION.DIV_NM AS DIVISION_NAME
, LOCATION.DIV_NBR AS DIVISION_NUMBER
, LOCATION.RGN_NBR AS REGION_NUMBER
, LOCATION.MKT_NBR AS MARKET_NUMBER
, COMP.STR_COMP_IND AS COMP_IND
, COMP.PY_STR_COMP_IND AS PRIOR_COMP_IND
, CALENDAR.FSCL_WK_DESC AS FISCAL_WEEK
, CALENDAR.FSCL_PRD_DESC AS FISCAL_PERIOD
, CALENDAR.FSCL_WK_END_DT AS END_DATE
, CALENDAR.FSCL_WK_BGN_DT AS BEGIN_DATE
, CALENDAR.FSCL_YR AS FISCAL_YEAR_NBR
, CALENDAR.FSCL_WK_NBR AS WEEK_NUMBER
, CALENDAR.FSCL_YR_WK_KEY_VAL AS FISCAL_KEY
, CALENDAR.LY_FYR_WK_KEY_VAL AS LY_FISCAL_KEY
, SUM(COALESCE(DTL.UNT_SLS,0)) AS UNITS
, SUM(COALESCE(DTL.EXT_RETL_AMT,0) + COALESCE(DTL.TOT_GDISC_DTL_AMT,0))
AS SALES
, SUM(CASE
WHEN 1=1 THEN (COALESCE(DTL.EXT_RETL_AMT,0) + COALESCE(DTL.TOT_GDISC_DTL_AMT,0)) * COMP.STR_COMP_IND
ELSE 0 END) AS COMP_SALES
, SUM(CASE
WHEN 1=1 THEN (COALESCE(DTL.UNT_SLS,0)) * COMP.STR_COMP_IND
ELSE 0 END) AS COMP_UNITS
, SUM(CASE
WHEN 1=1 AND SLS.SLS_DT = DATE_SUB(SLS.SLS_DT, INTERVAL 364 DAY)
THEN (COALESCE(DTL.EXT_RETL_AMT,0) +
COALESCE(DTL.TOT_GDISC_DTL_AMT,0)) * COMP.PY_STR_COMP_IND
ELSE NULL END)
AS LY_COMP_SALES
, SUM(CASE
WHEN 1=1 AND SLS.SLS_DT = DATE_SUB(SLS.SLS_DT, INTERVAL 364 DAY)
THEN (COALESCE(DTL.UNT_SLS,0)) * COMP.PY_STR_COMP_IND
ELSE NULL END)
AS LY_COMP_UNITS
, SUM(CASE
WHEN SLS.SLS_DT = DATE_SUB(SLS.SLS_DT, INTERVAL 371 DAY)
THEN (COALESCE(DTL.EXT_RETL_AMT,0) +
COALESCE(DTL.TOT_GDISC_DTL_AMT,0))
ELSE NULL END)
AS LY_SALES
, SUM(CASE
WHEN SLS.SLS_DT = DATE_SUB(SLS.SLS_DT, INTERVAL 371 DAY)
THEN (COALESCE(DTL.UNT_SLS,0))
ELSE NULL END)
AS LY_UNITS
FROM `pr-edw-views.SLS.POS_SLS_TRANS_DTL` AS SLS
INNER JOIN
UNNEST (SLS.DTL) AS DTL
JOIN `pr-edw-views.SHARED.MVNDR_HIER` AS MVNDR
ON DTL.DERIV_MVNDR.MVNDR_NBR = MVNDR.MVNDR_NBR
JOIN `pr-edw-views.SHARED.SKU_HIER_FD` AS SKU_HIER
ON SKU_HIER.SKU_NBR = DTL.SKU_NBR
AND SKU_HIER.SKU_CRT_DT = DTL.SKU_CRT_DT
JOIN `pr-edw-views.SHARED.LOC_HIER_FD` AS LOCATION
ON LOCATION.LOC_NBR = SLS.STR_NBR
JOIN `pr-edw-views.SHARED.CAL_PRD_HIER_FD` AS CALENDAR
ON CALENDAR.CAL_DT = SLS_DT
JOIN `pr-edw-views.SLS.STR_COMP_DAY` AS COMP
ON COMP.CAL_DT = CALENDAR.CAL_DT
AND COMP.STR_NBR = SLS.STR_NBR
WHERE CALENDAR.FSCL_WK_END_DT BETWEEN '2018-01-29' AND '2019-04-07'
AND SLS.SLS_DT BETWEEN '2018-01-29' AND '2019-04-07'
AND POS_TRANS_TYP_CD in ('S', 'R')
AND SKU_HIER.EXT_CLASS_NBR = '025-004'
AND MVNDR.MVNDR_NBR IN (74798, 60002238, 73059, 206820, 76009, 40263, 12879, 76722, 10830, 206823, 87752, 60052261, 70401, 51415, 51414)
AND SKU_HIER.LATEST_SKU_CRT_DT_FLG = TRUE
GROUP BY
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
I'm currently getting null values in my LY_SALES, LY_UNITS, LY_COMP_SALES and LY_COMP_UNITS columns, though I know there should have been locations with sales of those items from the same period the previous year. What I'm trying to get to is having those prior year values showing up along side the current year values. Any help would be hugely appreciated!
Thanks!

Such a condition can never be fulfilled : SLS.SLS_DT = DATE_SUB(SLS.SLS_DT, INTERVAL 371 DAY). Simply because a SLS_DT is not equal to SLS_DT-371.
You can pre-aggregate the table in a CTE (adding SLS_DT to the group by columns) and then replace the CASE with a join to the pre-aggregated table. Aim at something like this: and it will become something like (notice - no SUM in the case):
CASE WHEN AGGSLS.SLS_DT = DATE_SUB(SLS.SLS_DT, INTERVAL 371 DAY)
THEN (COALESCE(AGGSLS.SUM_EXT_RETL_AMT,0) +
COALESCE(AGGSLS.SUM_TOT_GDISC_DTL_AMT,0))
ELSE NULL END

Two things:
1) WHEN 1=1 can be expressed simply as WHEN TRUE, this way it is easier to move statements around without breaking the AND/OR chaining
2) to get the last year's sales. You can either omit the year from the final query and limit the output with a where clause or create a smaller table that has the sales this year, sales last year per week.
In my humble opinion sales last year for weeknum is the best option, as you can use it elsewhere. But it's pretty similar to what you wr
It would look something like:
SELECT CALENDAR.FSCL_WK_DESC as week_num,
sum(case when year = year(current_date()) then (COALESCE(DTL.UNT_SLS,0)) * COMP.STR_COMP_IND else 0 end) as this_year
sum(case when year = year(current_date())-1 then (COALESCE(DTL.UNT_SLS,0)) * COMP.STR_COMP_IND else 0 end) as last_year
And then you join back to the original table using week_num
Hope you find it useful
Cheers!

Related

Summing some columns in SQL Server from joined multiple table based on unique id

my query used to report monthly salary so when choose month from 1 - 2021 to 8 - 2021 it report like this i want to get same result for each badgenumber in one row for all choosen monthsI have the following query that collect data from multiple table
SELECT
dbo.USERINFO.BADGENUMBER ,
dbo.Emp_OV_LT.Overtime ,
dbo.Emp_OV_LT.Absent ,
(dbo.Emp_OV_LT.Late + dbo.Emp_OV_LT.Early) ,
ROUND((dbo.Salaries.B_S), 3),
ROUND((dbo.Salaries.T_A + dbo.Salaries.W_A + dbo.Salaries.L_A + dbo.Salaries.C_A), 3) ,
ROUND( dbo.Salaries.Overtime, 3),
ROUND(dbo.Salaries.Absent, 3),
ROUND( dbo.Salaries.Late_Deduction,3) ,
ROUND(dbo.Salaries.Total_Salary,3),
dbo.Salaries.SSN ,
ROUND(dbo.Salaries.n_salary,3)
FROM
dbo.USERINFO
INNER JOIN
dbo.Salaries ON dbo.USERINFO.USERID = dbo.Salaries.User_ID
INNER JOIN
dbo.Emp_Salary ON dbo.USERINFO.USERID = dbo.Emp_Salary.Emp_ID
INNER JOIN
dbo.Emp_OV_LT ON dbo.Emp_OV_LT.Emp_ID = dbo.USERINFO.USERID
WHERE
dbo.Salaries.Month = dbo.Emp_OV_LT.Month
AND dbo.Salaries.Month BETWEEN '1 - 2021' AND '8 - 2021'
AND dbo.Emp_OV_LT.year = '2021'
AND dbo.Salaries.ssn > 1
ORDER BY
dbo.userinfo.ssn,
dbo.Emp_OV_LT.Month
I want to sum all selected columns based on USERINFO.BADGENUMBER when it duplicated after change the month criteria for than one month
my query used to report monthly salary so when choose month from 1 - 2021 to 1 - 2021 it report like thisOk, I am going to take a stab at this, with what I believe you are trying to accomplish. I have been staring at your post for a while, and making some assumptions.
You stated
I want to sum all selected columns
At first I thought you wanted to sum all of the columns contained in the SELECT clause, but that wouldn't make sense. So I assume that you want to choose which columns you SUM. So let's say you want to SUM dbo.Emp_OV_LT.Overtime OVER each month / year for each dbo.USERINFO.BADGENUMBER. I believe you would do something like this.
WITH CTE AS
(
SELECT
dbo.USERINFO.BADGENUMBER ,
dbo.Emp_OV_LT.Overtime ,
dbo.Emp_OV_LT.Absent ,
(dbo.Emp_OV_LT.Late + dbo.Emp_OV_LT.Early) ,
ROUND((dbo.Salaries.B_S), 3),
ROUND((dbo.Salaries.T_A + dbo.Salaries.W_A + dbo.Salaries.L_A +
dbo.Salaries.C_A), 3) ,
ROUND( dbo.Salaries.Overtime, 3),
ROUND(dbo.Salaries.Absent, 3),
ROUND( dbo.Salaries.Late_Deduction,3) ,
ROUND(dbo.Salaries.Total_Salary,3),
dbo.Salaries.SSN ,
ROUND(dbo.Salaries.n_salary,3) ,
dbo.Salaries.Month,
dbo.Emp_OV_LT.year
FROM
dbo.USERINFO
INNER JOIN
dbo.Salaries ON dbo.USERINFO.USERID = dbo.Salaries.User_ID
INNER JOIN
dbo.Emp_Salary ON dbo.USERINFO.USERID = dbo.Emp_Salary.Emp_ID
INNER JOIN
dbo.Emp_OV_LT ON dbo.Emp_OV_LT.Emp_ID = dbo.USERINFO.USERID
WHERE
dbo.Salaries.Month = dbo.Emp_OV_LT.Month
AND dbo.Salaries.ssn > 1
ORDER BY
dbo.userinfo.ssn,
dbo.Emp_OV_LT.Month
)
SELECT DISTINCT *,
SUM(Overtime) OVER (PARTITION BY [Month],
BADGENUMBER]) AS SUM_OF_OverTime
FROM CTE
WHERE [Month] BETWEEN '1 - 2021' AND '8 - 2021' AND [year] = '2021'
Without having access to the database and tables, or some sample data, this is the best I could come up with.
Edit, you must add dbo.Salaries.Month to your original SELECT since you are partitioning by that field. Also, you should move the field dbo.Emp_OV_LT.year to your SELECT and you should use that field in your WHERE clause.

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.

Can't figure out how to get duplicate values out of table in SQL redshift

I am trying to return the foretasted value per item, per warehouse, per day and then add them up for the week. I am pulling from two tables depending on the demand date, but the issue is that both tables have a "creation_date" column with timestamps, so it's creating multiple raw_forecast entries per warehouse/item/day when I only want one. I tried to join on the creation dates, but because each table has different timestamps on the creation dates, SQL is returning both forecast quantities of that day. I just want whatever the largest forecast amount was for the day. Any help is so appreciated!
output columns: demand_date, item, fulfillment center, type quantity, raw_forecasts
there are multiple quantities and raw_forecast rows
SELECT
DISTINCT d.demand_date,
d.item,
r.fulfillment_center_external_id,
d.type,
d.quantity,
CASE WHEN d.type IN ('RAW') THEN MAX(DISTINCT d.quantity) ELSE 0 END as Raw_Forecast
FROM
f3_rsc.fab_reporting_demand_forecasts d
Left join f3_rsc.runs r on d.output_id = r.output_id
and TRUNC(d.creation_date) = TRUNC(r.creation_date)
where
1 = 1
and d.demand_date between to_date('{RUN_DATE_YYYY-MM-DD}', 'YYYY-MM-DD') + 11
and to_date('{RUN_DATE_YYYY-MM-DD}', 'YYYY-MM-DD') + 17
and d.type in ('RAW')
and requester_id = 'SWF-PRODUCTION'
and po_placement_status = 'SHOULD_CUT_PO'
and TRUNC(d.creation_date) > to_date('{RUN_DATE_YYYY-MM-DD}', 'YYYY-MM-DD') -3
GROUp BY
1,2,3,4,5
You are getting multiple rows because you are grouping on quantity and the quantities are different. Based on your description stop grouping on quantity (5 in your group by list) and take the MAX() of quantity in your select line. (You also don't need DISTINCT if the column is in the group by list.)
SELECT
d.demand_date,
d.item,
r.fulfillment_center_external_id,
d.type,
MAX(d.quantity),
CASE WHEN d.type IN ('RAW') THEN MAX(DISTINCT d.quantity) ELSE 0 END as Raw_Forecast
FROM
f3_rsc.fab_reporting_demand_forecasts d
Left join f3_rsc.runs r on d.output_id = r.output_id
and TRUNC(d.creation_date) = TRUNC(r.creation_date)
where
1 = 1
and d.demand_date between to_date('{RUN_DATE_YYYY-MM-DD}', 'YYYY-MM-DD') + 11
and to_date('{RUN_DATE_YYYY-MM-DD}', 'YYYY-MM-DD') + 17
and d.type in ('RAW')
and requester_id = 'SWF-PRODUCTION'
and po_placement_status = 'SHOULD_CUT_PO'
and TRUNC(d.creation_date) > to_date('{RUN_DATE_YYYY-MM-DD}', 'YYYY-MM-DD') -3
GROUp BY
1,2,3,4
Let me know if I have misread your situation.

Query optimisation - takes so long

I have this query that show turnover of the previous 24 months through the current date.
I'm using SQL SERVER 2008, my problem is that my query takes so long to be executed.
I'm using 2 table : Etablissement to get store name, and piece to get sum of Turnover of each etablissement.
result :
enter image description here
select ETAB.ET_ETABLISSEMENT as 'STORE CODE'
[Month-1]=(select CONVERT(DECIMAL(15,2),sum(gl_totalttcdev))
from piece left join ligne on gl_souche=gp_souche and gl_naturepieceg=gp_naturepieceg and gl_numero=gp_numero and gl_indiceg=gp_indiceg
left join etabliss as e on gp_etablissement=et_etablissement
left join ARTICLE on GL_CODEARTICLE = ARTICLE.GA_CODEARTICLE
where gp_naturepieceg='FFO'
and year(gp_datepiece) = year(DATEADD(MONTH,-1,GETDATE()))
and month(gp_datepiece) = MONTH( DATEADD(MONTH,-1,GETDATE()))
and gl_typearticle<>''
and gl_typearticle<>'FI'
and ETAB.ET_ETABLISSEMENT =e.ET_ETABLISSEMENT
and ETAB.ET_LIBELLE =e.ET_LIBELLE
group by gp_etablissement, et_libelle),
[Month-2]=(select CONVERT(DECIMAL(15,2),sum(gl_totalttcdev))
from piece left join ligne on gl_souche=gp_souche and gl_naturepieceg=gp_naturepieceg and gl_numero=gp_numero and gl_indiceg=gp_indiceg left join etabliss as e on gp_etablissement=et_etablissement
left join ARTICLE on GL_CODEARTICLE = ARTICLE.GA_CODEARTICLE
where gp_naturepieceg='FFO'
and year(gp_datepiece) = year(DATEADD(MONTH,-1,GETDATE()))
and month(gp_datepiece) = MONTH( DATEADD(MONTH,-1,GETDATE()))
and gl_typearticle<>''
and gl_typearticle<>'FI'
and ETAB.ET_ETABLISSEMENT =e.ET_ETABLISSEMENT
and ETAB.ET_LIBELLE =e.ET_LIBELLE
group by gp_etablissement, et_libelle),
[Some thing for the other months ..]
[Month-24]..,
from ETABLISS ETAB
Ther's any solution please to optimise my query ?
This is a comment that doesn't fit in the comments section.
I get that you have multiple queries going on and they are becoming slow. Let's take one at a time. For example:
select CONVERT(DECIMAL(15,2),sum(gl_totalttcdev))
from piece
left join ligne on gl_souche = gp_souche
and gl_naturepieceg = gp_naturepieceg and gl_numero=gp_numero
and gl_indiceg = gp_indiceg
left join etabliss as e on gp_etablissement = et_etablissement
left join ARTICLE on GL_CODEARTICLE = ARTICLE.GA_CODEARTICLE
where gp_naturepieceg = 'FFO'
and year(gp_datepiece) = year(DATEADD(MONTH,-1,GETDATE()))
and month(gp_datepiece) = MONTH( DATEADD(MONTH,-1,GETDATE()))
and gl_typearticle <> ''
and gl_typearticle <> 'FI'
and ETAB.ET_ETABLISSEMENT = e.ET_ETABLISSEMENT
and ETAB.ET_LIBELLE = e.ET_LIBELLE
group by gp_etablissement, et_libelle
First of all, in order to optimize it it's critical for us to know where each column is coming from. So... please add the prefix for all the columns you are mentioning. We can't really guess the tables for each column.
For every record those 2 functions get executed, year(DATEADD(MONTH,-1,GETDATE())) and MONTH( DATEADD(MONTH,-1,GETDATE())) it always gives a constant value , stored those value in a variable and use those variable in query will help to improve the speed.
Your query as written is quite confusing and probably why only one other has even offered a solution. Now lets try to simplify and clarify what you are asking for. Correct me if anything is incorrect.
You need a query that shows the totals over the last 13 month period grouped by each
store code. The first month represents the month prior to current and going back 13 months (or 24 as you indicated).
Now your query. You are querying from your ETABLISS table and grouping by the gp_etablisssement, et_libelle.
By doing a google translate of French to English
Etablissement = Establishement
Libelle = Wording
So I am GUESSING your etablissement is more of an ID key that is unique and the Libelle is the label to be shown for the given etablissement.
Ex:
etablissement libelle
1 Business A
2 Business B
3 Business C
So the label will always be a direct correlation with the etablissement (ID) field.
Lets first simplify. You want a sum of all linge values that qualify per etablissement with some filters. Start with that, and the piece table is the basis of the date range criteria. The Piece table has the etablissement field you ultimately want to group by
select
p.gp_etablissement,
sum(l.gl_totalttcdev) SumPerEtablissement
from
Piece P
JOIN Linge L
-- all criteria specifically limiting the LINGE records
on P.gp_souche = L.gl_souche
and p.gp_naturepieceg = l.gl_naturepieceg
and p.gp_numero = l.gl_numero
and p.gp_indiceg = l.gl_indiceg
and l.gl_typearticle <> ''
and l.gl_typearticle <> 'FI'
where
-- simple to get all records for past 13 months for now just to show summary
p.gp_DatePiece > dateadd( month, -13, getdate())
group by
p.gp_etablissement
So from above, we have one result table per establishement with its total over the entire 13 months. Now, you want it broken down per month... add an extra column showing the month/year as a column and group by. While we are here, we can join to the etablissement table to pull the description/label you may want to show.
One extra item I am throwing into this is the months difference from the current date so we always have a constant sequence to organize totals by. Use DateDiff(). This will help for your pivot result pulling all data on one row per etablissement.
select
p.gp_etablissement,
e.et_libelle,
-- Example: 2019-04-27 would return '2019-04-27'
CONVERT(CHAR(7),p.gp_DatePiece,120) as YrMonth,
-- nice thing about datediff by month. It properly detects
-- '2019-04-01' from '2019-03-31' as 1 month apart even though 1 day
-- so ANY Date within the YrMonth will be within the given month.
min( datediff( month, p.gp_DatePiece, getdate() )) as MonthsPrior,
-- now the column getting summed
sum(l.gl_totalttcdev) SumPerEtabliss
from
Piece P
JOIN Linge L
-- all criteria specifically limiting the LINGE records
on P.gp_souche = L.gl_souche
and p.gp_naturepieceg = l.gl_naturepieceg
and p.gp_numero = l.gl_numero
and p.gp_indiceg = l.gl_indiceg
and l.gl_typearticle <> ''
and l.gl_typearticle <> 'FI'
JOIN Etabliss e
on p.gp_etablissement = e.et_etablissement
where
-- simple to get all records for past 13 months for now just to
-- show summary, or change to 24 months as so needed
p.gp_DatePiece > dateadd( month, -13, getdate())
group by
p.gp_etablissement,
e.et_libelle,
CONVERT(CHAR(7),GETDATE(),120)
the above results may have something like...
gp_etablissement et_libelle YrMonth MonthsPrior SumPerEtabliss
establissID1 libell1 2018-12 4 382
establissID1 libell1 2019-01 3 123
establissID1 libell1 2019-02 2 821
establissID1 libell1 2019-03 1 467
establissID2 libell2 2018-12 4 532
establissID2 libell2 2019-01 3 221
establissID2 libell2 2019-02 2 629
establissID2 libell2 2019-03 1 395
Now, you can build a cross-tab query aggregating each column based on how many month away they are from the original month in question. Since you want 24 months, you would have to copy/paste the sum() values below to represent the remaining columns.
select
pq.gp_etablissement,
pq.et_libelle,
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 1 then 1 else 0 end ) as [Month 1],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 2 then 1 else 0 end ) as [Month 2],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 3 then 1 else 0 end ) as [Month 3],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 4 then 1 else 0 end ) as [Month 4],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 5 then 1 else 0 end ) as [Month 5],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 6 then 1 else 0 end ) as [Month 6],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 7 then 1 else 0 end ) as [Month 7],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 8 then 1 else 0 end ) as [Month 8],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 9 then 1 else 0 end ) as [Month 9],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 10 then 1 else 0 end ) as [Month 10],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 11 then 1 else 0 end ) as [Month 11],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 12 then 1 else 0 end ) as [Month 12],
sum( pq.SumPerEtabliss * case when pq.MonthsPrior = 13 then 1 else 0 end ) as [Month 13]
from
( THE ENTIRE QUERY ABOVE THAT DOES THE GROUP BY ) PQ
group by
pq.gp_etablissement,
pq.et_libelle
order by
pq.et_libelle

Use sum case sql in Crystal command to show sales in this year and last year?

I am using the sql code below in the Crystal "Command" to display current years sales units and dollars (all closed sales versus sales closed using a discount). I need to add the last years unit sales qty. Does anyone have any idea the nest way to do this? Thanks to anyone who has any ideas.
Code:
SELECT
convert(char(4),datepart(yy,m.close_dt)) +
right('00' + convert(varchar,datepart(m,m.close_dt)),2) AS SortMonth,
replace(right(convert(varchar(11), m.close_dt, 106), 8), ' ', '-') AS DisplayMonth,
sum(case when lt.um_ch177= 'BAWRM' then 1 else 0 end) as Close_Units_Disc ,
sum(case when lt.um_ch177= 'BAWRM' then m.tot_ln_amt else 0 end) as Close_Dollars_Disc,
sum(case when m.close_dt >= '{?Date1}'
and m.close_dt <= '{?Date2}' then 1 else 0 end) as Close_Units_All,
sum(case when m.close_dt >= '{?Date1}'
and m.close_dt <= '{?Date2}' then tot_ln_amt else 0 end) as Close_Dollars_All
FROM
pro2sql.lt_master m WITH (NOLOCK)
LEFT OUTER JOIN pro2sql.ltuch_master lt WITH (NOLOCK) ON m.lt_acnt=lt.lt_acnt
WHERE
m.stage = 60
and m.loan_purpose <> 7
and m.app_number <> 0
and m.brch_entry {?BranchList}
and m.close_dt >= '{?Date1}'
and m.close_dt <'{?Date2}'
Group by
convert(char(4),datepart(yy,m.close_dt)) + right('00' + convert(varchar,datepart(m,m.close_dt)),2)
,replace(right(convert(varchar(11), m.close_dt, 106), 8), ' ', '-')
I can't upload a pic - not sure if this is going to be a jumble but here is the output - the last two columns are what I need to add:
DisplayMonth Close_Units_Disc Close_Dollars_Disc Close_Units_All Close_Dollars_All %Units %Dollars DisplayMonth LY CloseUnits All
Feb-2014 115 $48,919,800 190 $83,942,650 61% 58% Feb-2013
Mar-2014 202 $91,077,780 238 $109,300,903 85% 83% Mar-2013
Apr-2014 219 $89,157,481 238 $95,892,509 92% 93% Apr-2013
"Enterprise system" is not an adequate answer. What type of database is this? Oracle? Microsoft (and which version if its MSS)? Something else?
That may influence what solutions are possible, or at least the syntax
You have at least two options off the top of my head though:
One:
Expand the date range to pull in the previous year's data;
modify all of your sum case statements to include the current year only.
add an additional sum case with appropriate date range conditions for the previous year's units.
Two:
duplicate your query as a subquery for the previous year and join to it
e.g:
LEFT OUTER JOIN (
replace(right(convert(varchar(11), m.close_dt, 106), 8), ' ', '-') AS DisplayMonth,
SELECT sum(case when lt.um_ch177= 'BAWRM' then 1 else 0 end) as Close_Units_Disc
FROM pro2sql.lt_master m WITH (NOLOCK)
LEFT OUTER JOIN pro2sql.ltuch_master lt WITH (NOLOCK) ON m.lt_acnt=lt.lt_acnt
WHERE m.stage = 60 and m.loan_purpose <> 7 and m.app_number <> 0
and m.brch_entry {?BranchList}
and m.close_dt >= '{?Date3}' --//#last year's start date
and m.close_dt <'{?Date4}'` --//#last year's end date
Group by
convert(char(4),datepart(yy,m.close_dt)) +
right('00' + convert(varchar,datepart(m,m.close_dt)),2),
replace(right(convert(varchar(11), m.close_dt, 106), 8), ' ', '-')
) as lastYear
on replace(right(convert(varchar(11), m.close_dt, 106), 8), ' ', '-') = lastYear.DisplayMonth
If you go with one of these, you may want to consult with a DBA at your company to see which would be more efficient... I don't know if running a query with a larger result is more intensive than running nearly identical queries twice, and that too may change form one architecture to another (or even just with different environment variables/parameters configured on the server)