Select max field value from a group of records? - abap

I need to get the lines with the highest EXBEL registers from an itab for every different VKONT.
The result table may contain several lines for the same VKONT value if this latter one has several lines in the source table with the same highest EXBEL value.
Actual source table:
SPARTE VKONT EXBEL
05 800000008422 1NSN150900000058
L2 800000008422 1NSN150900000058
05 800000008422 1NSN150900000037
L2 800000008422 1NSN150900000037
05 800000008422 1NSN150900000013
L2 800000008422 1NSN150900000013
05 800000008415 1HSN151200000009
S1 800000008415 1HSN151200000009
05 800000008415 1HSN151200000008
S1 800000008415 1HSN151200000008
L1 800000008422 1NSN150900000050
L1 800000008422 1NSN150900000029
L1 800000008422 1NSN150900000023
05 800000008415 1HSN151200000012
S1 800000008415 1HSN151200000012
05 800000008422 1NSN150900000058
L2 800000008422 1NSN150900000058
05 800000008415 1HSN151200000009
S1 800000008415 1HSN151200000009
Expected result table:
SPARTE VKONT EXBEL
05 800000008422 1NSN150900000058
L2 800000008422 1NSN150900000058
05 800000008415 1HSN151200000012
S1 800000008415 1HSN151200000012
I tried different solutions but didn't work.
Any help will be appreciated.
Raúl.

Starting from 7.52, you can select on internal tables. Sample code as below.
TYPES:
BEGIN OF ty_s_value,
sparte TYPE char2,
vkont TYPE char12,
exbel TYPE char16,
END OF ty_s_value.
TYPES:
ty_t_value TYPE STANDARD TABLE OF ty_s_value .
DATA:
lt_value TYPE ty_t_value.
lt_value = VALUE #(
( sparte = '05' vkont = '800008422' exbel = '0000000000000001')
( sparte = 'l2' vkont = '800008422' exbel = '0000000000000002')
( sparte = 'l2' vkont = '800008422' exbel = '0000000000000004')
( sparte = '05' vkont = '800008423' exbel = '0000000000000003')
( sparte = 'l2' vkont = '800008423' exbel = '0000000000000002')
( sparte = 'l2' vkont = '800008423' exbel = '0000000000000005')
).
SELECT FROM #lt_value AS a FIELDS a~sparte, a~vkont, MAX( a~exbel ) AS exbel
GROUP BY a~sparte, a~vkont
ORDER BY a~sparte, a~vkont INTO TABLE #DATA(result).

Gather to Hashed table
This works with any release currently supported, is faster1 than a SORT + DELETE ADJACENT DUPLICATES, and leaves the original table intact.
FIELD-SYMBOLS: <fs_itab> LIKE LINE OF lt_original.
DATA: ls_itab LIKE LINE OF lt_original,
lt_hashed TYPE HASHED TABLE OF itab WITH UNIQUE KEY vkont.
LOOP AT lt_original INTO ls_itab.
READ TABLE lt_hashed ASSIGNING <fs_itab>
WITH KEY vkont = ls_itab-vkont.
IF sy-subrc = 0.
IF ls_itab-exbel > <fs_itab>-exbel.
<fs_itab>-exbel = ls_itab-exbel.
<fs_itab>-sparte = ls_itab-sparte. "remove this if not needed"
ENDIF.
ELSE.
INSERT ls_itab INTO TABLE lt_hashed.
ENDIF.
ENDLOOP.
SORT + DAD has a speed scaling of O(n*log(n)), while this has O(n)

Here is REDUCE solution, REDUCE operator is iavailable since ABAP 7.40 SP08.
TYPES: BEGIN OF ty_s_value,
sparte TYPE char2,
vkont TYPE char12,
exbel TYPE char16,
END OF ty_s_value.
TYPES: ty_t_value TYPE STANDARD TABLE OF ty_s_value WITH EMPTY KEY.
DATA(lt_tab) =
VALUE ty_t_value(
( sparte = '05' vkont = '800000008422' exbel = '1NSN150900000058')
( sparte = 'L2' vkont = '800000008422' exbel = '1NSN150900000058')
( sparte = '05' vkont = '800000008422' exbel = '1NSN150900000037')
( sparte = 'L2' vkont = '800000008422' exbel = '1NSN150900000037')
( sparte = '05' vkont = '800000008422' exbel = '1NSN150900000013')
( sparte = 'L2' vkont = '800000008422' exbel = '1NSN150900000013')
( sparte = '05' vkont = '800000008415' exbel = '1HSN151200000009')
( sparte = 'S1' vkont = '800000008415' exbel = '1HSN151200000009')
( sparte = '05' vkont = '800000008415' exbel = '1HSN151200000008')
( sparte = 'S1' vkont = '800000008415' exbel = '1HSN151200000008')
( sparte = 'L1' vkont = '800000008422' exbel = '1NSN150900000050')
( sparte = 'L1' vkont = '800000008422' exbel = '1NSN150900000029')
...
).
DATA(lt_result) =
VALUE ty_t_value( FOR GROUPS <group_key> OF <wa> IN lt_tab
GROUP BY ( sparte = <wa>-sparte vkont = <wa>-vkont )
LET max2 =
REDUCE #( INIT max =
VALUE ty_s_value( )
FOR <m> IN GROUP <group_key>
NEXT max = COND #( WHEN <m>-exbel > max-exbel THEN <m> ELSE max ) )
IN ( max2 ) ).
BTW, your expected resultset is missing L1 line, I assume in your selection you respect not only VKONT but SPARTE too.

You can use a SORT followed by a DELETE ADJACENT DUPLICATES since the latter will delete all rows in certain groups of rows, except for the first row of the group.
SORT itab BY vkont exbel DESCENDING. " Group by VKONT and put highest EXBEL in the group first
DELETE ADJACENT DUPLICATES FROM itab COMPARING VKONT.
There might be more efficient ways if you need to keep the original itab intact.

Related

Why isn't this CTE recognized?

I'm trying to pull in student majors to this sql by using a CTE, but when I try to add the CTE fields, or join the CTE with an implicit join, which works fine in other queries, oracle throws the error 'invalid identifier'. Any thoughts?
This is only the first part of many unions in this sql but I've seen examples where a CTE works fine with unions so I don't think thats it, and besides that when I run this code without the unions I get the same errors, 'invalid identifier'.
with major (pidm, major) as
(
select
a.sgbstdn_pidm,
a.sgbstdn_majr_code_1
from
sgbstdn a
where a.sgbstdn_term_code_eff = (select
max(b.sgbstdn_term_code_eff)
from
sgbstdn b
where
a.sgbstdn_pidm = b.sgbstdn_pidm
and b.sgbstdn_term_code_eff <= '202004'
and b.sgbstdn_term_code_eff > '202001')
)
select --authorized aid
spriden_id id
,spriden_last_name ||', '||spriden_first_name || ' '|| spriden_mi "Name"
,major.major "Major"
,rpratrm_fund_code "Fund"
,rpratrm_period "Period"
,rpratrm_offer_amt "Offer"
,rpratrm_accept_amt "Accept"
,null "Loan Net Orig Amt"
,RPRATRM_AUTHORIZE_AMT "Authorized"
,rpratrm_paid_amt "Paid"
,c.hr "Census Hours"
,r.hr "Enrolled Hours"
,c.con "Consortium"
,b.pbcode "P Budget Code"
,b.pbcode_locked "P Budget Code Locked?"
,b.b_locked "Budget Locked"
--,astd.astd "Academic Standing"
,s.sap "SAP Code"
,s.term "SAP Term"
,decode(h.pidm, null, 'No', 'Yes') "Holds"
,admit.admit "Admitted?"
from
spriden
,rpratrm
--,(select SGVSTDN_pidm pidm, sgvstdn_astd_desc astd from SGVSTDN where SGVSTDN_term_code = '202003') astd
--admitted?
,(select
sgbstdn_pidm pidm
,case
when sgbstdn_levl_code like 'N%' then 'No'
when sgbstdn_levl_code is null then 'No Student Record found this term'
else 'Yes'
end admit
from
sgbstdn
where
sgbstdn_term_code_eff = '202003') admit
--HOLDS
,(select
rorhold_pidm pidm
from
rorhold
where
to_char(sysdate, 'YYYYMMDD') <= to_char(RORHOLD_TO_DATE, 'YYYYMMDD')
and to_char(sysdate, 'YYYYMMDD') >= to_char(RORHOLD_FROM_DATE, 'YYYYMMDD')
) h
--SAP
,(select
a.rorsapr_pidm pidm
,a.rorsapr_term_code term
,a.rorsapr_sapr_code sap
from
rorsapr a
where
a.rorsapr_term_code = (select max(b.rorsapr_term_code) from rorsapr b where a.rorsapr_pidm = b.rorsapr_pidm and b.rorsapr_term_code <= '202003')) s
--Period Budget Code Lock/FREEZE
,(select
rbrapbg_pidm pidm
,RBRAPBG_PBGP_CODE pbcode
,decode(RBRAPBG_PBGP_CODE_LOCK_IND, 'Y', 'Yes', 'No') pbcode_locked
,decode(RBRAPBG_BUDGET_FREEZE_IND, 'Y', 'Yes', 'No') b_locked
from
rbrapbg
where
RBRAPBG_RUN_NAME = 'ACTUAL'
and RBRAPBG_PERIOD = '202003'
and RBRAPBG_AIDY_CODE = '2021') b
,(select
rorenrl_pidm pidm
,rorenrl_term_code term
,RORENRL_FINAID_ADJ_HR hr
,RORENRL_CONSORTIUM_IND con
from
rorenrl
where
rorenrl_enrr_code = 'STANDARD'
and rorenrl_term_code like '202003') c
,(select
sfrstcr_pidm pidm
,sfrstcr_term_code term
,sum(sfrstcr_credit_hr) hr
from
sfrstcr
where
sfrstcr_term_code like '202003'
and sfrstcr_rsts_code in (select stvrsts_code from stvrsts where STVRSTS_INCL_SECT_ENRL = 'Y')
group by sfrstcr_pidm, sfrstcr_term_code) r
where
spriden_change_ind is null
and spriden_pidm = rpratrm_pidm
and rpratrm_aidy_code = '2021'
and RPRATRM_AUTHORIZE_AMT is not null
and rpratrm_pidm = c.pidm(+)
and rpratrm_period = c.term(+)
and rpratrm_pidm = r.pidm(+)
and rpratrm_period = r.term(+)
and rpratrm_period = '202003'
and rpratrm_pidm = b.pidm(+)
and rpratrm_pidm = s.pidm(+)
and rpratrm_pidm = h.pidm(+)
and rpratrm_pidm = admit.pidm(+)
and rpratrm_pidm = major.pidm
--and rpratrm_pidm = astd.pidm(+)
and not exists (select 'asdf' from SGVSTDN a where a.sgvstdn_pidm = rpratrm_period and a.SGVSTDN_term_code = (select max(b.sgvstdn_term_code) from sgvstdn b where b.sgvstdn_term_code <= '202003' and b.sgvstdn_astd_desc is not null and a.sgvstdn_pidm = b.sgvstdn_pidm) and a.sgvstdn_astd_code = 'SU')
and (r.hr is not null or c.hr is not null)
and not exists (select 'afd' from rorstat where rorstat_pidm = rpratrm_pidm and rorstat_aidy_code = rpratrm_aidy_code and RORSTAT_DISB_REQ_COMP_DATE is null)
and not exists (select 'afd' from rrrareq where rrrareq_pidm = rpratrm_pidm and rrrareq_aidy_code = rpratrm_aidy_code and rrrareq_fund_code = rpratrm_fund_code and RRRAREQ_SAT_IND = 'N')
--this will exclude those without loans should be a loan only query. if chur is auth but mpn not done, student won't show up
and exists (select 'asdf' from rlrdlor, rlrdldb where RLRDLOR_PIDM = rlrdldb_pidm and RLRDLOR_PIDM = rpratrm_pidm and RLRDLOR_LOAN_NO = rlrdldb_loan_no and RLRDLOR_AIDY_CODE = rlrdldb_aidy_code
and rlrdlor_aidy_code = rpratrm_aidy_code and RLRDLOR_FUND_CODE = rlrdldb_fund_code and rlrdlor_fund_code = rpratrm_fund_code and RLRDLOR_MPN_LINKED_IND = 'Y')
Adding the CTE to the from clause made it work. Forgot about that little detail.

Fill range table from itab using LET

I found this code for filling a range table (source is already offline):
DATA lr_vkorg TYPE RANGE OF vkorg.
TYPES: lr_range_t TYPE RANGE OF vkorg.
lr_vkorg = VALUE lr_range_t(
LET s = 'I'
o = 'EQ'
IN sign = s
option = o
( low = '1100' )
( low = '1200' )
( low = '1300' )
( low = '1400' )
( low = '1500' )
).
But instead of doing something like this:
( low = '1' )
( low = '2' )
...
I want to fill it with the values of an internal table ['1', '2', ...].
Does anyone have an idea how to do this?
Update: This is how I did it based on the answer:
DATA:
lt_itab TYPE TABLE OF string,
lt_range_itab TYPE RANGE OF string
.
APPEND '1' TO lt_itab.
APPEND '2' TO lt_itab.
APPEND '3' TO lt_itab.
APPEND '4' TO lt_itab.
lt_range_itab = VALUE #(
FOR <ls_itab> IN lt_itab
( sign = 'I'
option = 'EQ'
low = <ls_itab> )
).
You can declare macro utilizing your declared structures like this:
DEFINE range.
lr_vkorg = VALUE lr_range_t( BASE lr_vkorg ( sign = 'I' option = 'EQ' low = &1 ) ).
END-OF-DEFINITION.
And then fill your ranges with this one-liner:
range: '1100', '1200', '1300', '1400', '1500', '1600'.
If we speak about filling range from itab, use this statement:
lr_vkorg = VALUE #( FOR ls_vkorg IN gt_vkorg
( sign = 'I'
option = 'EQ'
low = ls_vkorg-vkorg )
).
lr_vkorg = VALUE # ( sign = 'I' option = 'EQ' ( low = '1100' ) ( low = '1200' )
( low = '1300' ) ( low = '1400' ) ( low = '1500' ) ).

ORA-00907: missing right parenthesis - Following error is coming when i am putting Case statement in where clause

$ The following code only highlights the where clause of BI Publisher query, in which i have an input paramter :P_COUNTRY, through which i am recieving the values either as PH/MY/CN on the basis of that i have to put the where clause.
WHERE PRG.PAYROLL_RELATIONSHIP_ID = PEU.PAYROLL_RELATIONSHIP_ID
AND PEU.ELEMENT_ENTRY_ID = PEE.ELEMENT_ENTRY_ID
AND PEE.ELEMENT_TYPE_ID = PET.ELEMENT_TYPE_ID
AND PRG.ASSIGNMENT_ID = PAAF.ASSIGNMENT_ID
AND PPSV.PERSON_ID = PPNF.PERSON_ID
AND PPSV.PERSON_ID = PAAF.PERSON_ID
--AND PPNF.PERSON_ID = CCPP.PERSON_ID
AND PAAF.PRIMARY_ASSIGNMENT_FLAG = 'Y'
AND PAAF.ASSIGNMENT_TYPE = 'E'
AND PAAF.PERIOD_OF_SERVICE_ID = PPOS.PERIOD_OF_SERVICE_ID
AND PPOS.PERSON_ID = PAAF.PERSON_ID
AND PPOS.LEGAL_ENTITY_ID = PLE.ORGANIZATION_ID
AND PPNF.NAME_TYPE = 'GLOBAL'
AND (CASE WHEN (:P_Country = 'PH') THEN (UPPER(PET.ELEMENT_NAME) IN ('PHCTC EXT'))
WHEN (:P_Country = 'MY') THEN (UPPER(PET.ELEMENT_NAME) IN ('MYCTC EXT'))
WHEN (:P_Country = 'CN') THEN (UPPER(PET.ELEMENT_NAME) IN ('CNCTC EXT'))
END)
AND PAAF.GRADE_ID = PG.GRADE_ID
What you want would be more like:
WHERE PRG.PAYROLL_RELATIONSHIP_ID = PEU.PAYROLL_RELATIONSHIP_ID
AND PEU.ELEMENT_ENTRY_ID = PEE.ELEMENT_ENTRY_ID
AND PEE.ELEMENT_TYPE_ID = PET.ELEMENT_TYPE_ID
AND PRG.ASSIGNMENT_ID = PAAF.ASSIGNMENT_ID
AND PPSV.PERSON_ID = PPNF.PERSON_ID
AND PPSV.PERSON_ID = PAAF.PERSON_ID
--AND PPNF.PERSON_ID = CCPP.PERSON_ID
AND PAAF.PRIMARY_ASSIGNMENT_FLAG = 'Y'
AND PAAF.ASSIGNMENT_TYPE = 'E'
AND PAAF.PERIOD_OF_SERVICE_ID = PPOS.PERIOD_OF_SERVICE_ID
AND PPOS.PERSON_ID = PAAF.PERSON_ID
AND PPOS.LEGAL_ENTITY_ID = PLE.ORGANIZATION_ID
AND PPNF.NAME_TYPE = 'GLOBAL'
AND UPPER(PET.ELEMENT_NAME) =
CASE WHEN :P_Country = 'PH' THEN 'PHCTC EXT'
WHEN :P_Country = 'MY' THEN 'MYCTC EXT'
WHEN :P_Country = 'CN' THEN 'CNCTC EXT'
END
AND PAAF.GRADE_ID = PG.GRADE_ID
There is no need for all of the parenthesis you were using, and no need for the use of IN when you are only comparing one value.
Another alternative would be to do away with the CASE and use a series of grouped conditional clauses:
WHERE PRG.PAYROLL_RELATIONSHIP_ID = PEU.PAYROLL_RELATIONSHIP_ID
AND PEU.ELEMENT_ENTRY_ID = PEE.ELEMENT_ENTRY_ID
AND PEE.ELEMENT_TYPE_ID = PET.ELEMENT_TYPE_ID
AND PRG.ASSIGNMENT_ID = PAAF.ASSIGNMENT_ID
AND PPSV.PERSON_ID = PPNF.PERSON_ID
AND PPSV.PERSON_ID = PAAF.PERSON_ID
--AND PPNF.PERSON_ID = CCPP.PERSON_ID
AND PAAF.PRIMARY_ASSIGNMENT_FLAG = 'Y'
AND PAAF.ASSIGNMENT_TYPE = 'E'
AND PAAF.PERIOD_OF_SERVICE_ID = PPOS.PERIOD_OF_SERVICE_ID
AND PPOS.PERSON_ID = PAAF.PERSON_ID
AND PPOS.LEGAL_ENTITY_ID = PLE.ORGANIZATION_ID
AND PPNF.NAME_TYPE = 'GLOBAL'
AND (
(:P_Country = 'PH' AND UPPER(PET.ELEMENT_NAME) = 'PHCTC EXT') OR
(:P_Country = 'MY' AND UPPER(PET.ELEMENT_NAME) = 'MYCTC EXT') OR
(:P_Country = 'CN' AND UPPER(PET.ELEMENT_NAME) = 'CNCTC EXT')
)
AND PAAF.GRADE_ID = PG.GRADE_ID
Case Expressions are Not Well Suited for the Where Clause
You are using a searched case expression. The results component of the case expression does not accept a boolean.
Here is the existing condition:
AND (CASE WHEN (:P_Country = 'PH') THEN (UPPER(PET.ELEMENT_NAME) IN ('PHCTC EXT'))
WHEN (:P_Country = 'MY') THEN (UPPER(PET.ELEMENT_NAME) IN ('MYCTC EXT'))
WHEN (:P_Country = 'CN') THEN (UPPER(PET.ELEMENT_NAME) IN ('CNCTC EXT'))
END)
I translate your case statement in words to the following:
the bind variable, :P_Country, is being checked to see if it is in the PET.ELEMENT_NAME. More so, it is just the first segment of that ELEMENT_NAME.
In SQL, I write:
AND UPPER(PET.ELEMENT_NAME) = :P_Country ||'CTC EXT'
Ideally it would be good to know if ELEMENT_NAME is all upper, lower, proper or mixed. One could modify the case of the bind variable to correspond with the ELEMENT_NAME.
You will get better performance.
for Example if we know ELEMENT_NAME is always UPPER case, we could do this:
AND PET.ELEMENT_NAME = UPPER(:P_Country) ||'CTC EXT'

SQL - how to consolidate Or statements

There must be a better way to write the query below, if you have any suggestions please let me know.
SELECT *
FROM
[eSDR_Full].[dbo].[course_register]
WHERE
broad_field_code = '08'
AND narrow_field_code = '05'
AND detail_field_code = '05'
AND end_month is null
AND disabled <> 'Y'
OR broad_field_code = '08'
AND narrow_field_code = '05'
AND detail_field_code = '05'
AND end_month > '201512'
AND disabled <> 'Y'
OR broad_field_code = '08'
AND narrow_field_code = '05'
AND detail_field_code = '05'
AND end_month is null
AND disabled is null
OR broad_field_code = '08'
AND narrow_field_code = '05'
AND detail_field_code = '05'
AND end_month > '201512'
AND disabled is null
ORDER BY
end_month DESC
First refactoring:
SELECT *
FROM [eSDR_Full].[dbo].[course_register]
Where (broad_field_code = '08' and narrow_field_code = '05' and detail_field_code = '05')
and ( (end_month is null and disabled <> 'Y')
or (end_month > '201512' and disabled <> 'Y')
or (end_month is null and disabled is null)
or (end_month > '201512' and disabled is null))
order by end_month desc
This makes it clear that you have a common prefix to all the conditions.
Second refactoring takes advantage of other commonalities:
SELECT *
FROM [eSDR_Full].[dbo].[course_register]
Where (broad_field_code = '08' and narrow_field_code = '05' and detail_field_code = '05')
and (end_month is null or end_month > '201512')
and (disabled is null or disabled <> 'Y')
order by end_month desc
Now it's clear that we just have a series of stacked ANDs based on simple conditions of one column each, so we can do this:
SELECT *
FROM [eSDR_Full].[dbo].[course_register]
Where broad_field_code = '08'
and narrow_field_code = '05'
and detail_field_code = '05'
and coalesce(end_month, '999999') > '201512'
and coalesce(disabled, 'N') <> 'Y')
order by end_month desc
This should work:
SELECT *
FROM [eSDR_Full].[dbo].[course_register]
WHERE broad_field_code = '08'
AND narrow_field_code = '05'
AND detail_field_code = '05'
AND (end_month IS NULL OR end_month > '201512')
AND ISNULL(disabled,'') != 'Y'
ORDER BY end_month DESC

Can someone show me how to add these fields to this Stored Procedure

I have a SP that was built for us that does a summary statement of tables in our DB. What I am trying to do is make it so the SP also pulls that last year/month of data as well based on the date entered. Below is the SQL code I am working with. What I am trying to get is a total and Volume field that is the sum based on the date parameter entered minus 1 month.
For example:
If I put in 2013 10 01 start and 2013 10 31 end I would get the total and volume for 2013-10-01 to 2013-10-31 and in 2 separate columns the total and volume for 2013-09-01 to 1013-09-30
Code
(
#Start DATETIME,
#End DATETIME
)
AS
DECLARE
#reference int,
#sSQL VARCHAR(2000)
BEGIN
select Convert(datetime,Cast(edi.Creation_dt as varchar(8)),103) as Date, ia.xref_no_tx, la.ldc_acct_no, la.serv_loc_nm
, a.acct_nm, c.company_last_nm
, Case RG.Rate_cd
When 'DLS' then 'HEDGE'
When 'STL' then 'STL'
WHen 'SPOT BILLING' then 'SPOT'
WHen 'SL SPOT' then 'STL SPOT'
Else null
End as Acct_type
, Convert(datetime,Cast(ia.start_dt as varchar(8)),103)as Start_dt
, Convert(datetime,Cast(ia.end_dt as varchar(8)),103) as End_dt
, edi.trans_sub_ty as Inv_type
, max( case when la.class_ty_cd = 'COMM' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'FEES' then th.trans_qty
when la.class_ty_cd = 'MUNI' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'EXCS' then th.trans_qty
when la.class_ty_cd <> 'COMM' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'BASE' then th.trans_qty
else 0 end) as Volume
, sum(th.trans_am) as Total
from invoice_advise_relate iar, transaction_history th
,invoice_advise ia, ldc_account la, account a, customer c, edi_transaction edi
, (select max(edi_trans_id) as m_edi_trans, relate_id from edi_transaction where class_nm = 'cInvoiceAdvise' group by relate_id) as edic
, (Select max(rating_group_id) as m_rate, ldc_acct_id from rating_group group by ldc_acct_Id) as C_Rate
, rating_group rg
where iar.trans_id = th.trans_id
and th.cancel_in = 'N'
and th.trans_ty_cd not in ('PAY', 'ANC')
and iar.inv_adv_id = ia.inv_adv_id
and ia.ldc_acct_id = la.ldc_acct_id
and la.acct_id = a.acct_id
and a.cust_id = c.cust_id
and la.ldc_acct_no not like 'E%'
and edi.Creation_dt >= convert(varchar,#Start,112)
and edi.Creation_dt <= convert(varchar,#End,112)
and edi.relate_id = ia.inv_adv_id
and edic.m_edi_trans = edi.edi_trans_id
and edi.response_cd = ''
and rg.rating_group_id = C_Rate.M_Rate
and C_Rate.LDC_Acct_Id = la.ldc_Acct_Id
and edi.trans_sub_ty <> '00'
group by edi.Creation_dt, ia.xref_no_tx, la.ldc_acct_no,la.serv_loc_nm, a.acct_nm, c.company_last_nm, ia.start_dt, ia.end_dt,edi.trans_sub_ty, rg.rate_cd
Start off by declaring and initializing a start date for the previous month.
DECLARE #PrevStart datetime
SELECT #PrevStart = dateadd(month, -1, #Start)
In your WHERE clause substitute the previous start date for start date so that you include last month's data as well as this month's.
and edi.Creation_dt >= convert(varchar,#PrevStart,112)
and edi.Creation_dt <= convert(varchar,#End,112)
Then you filter last month's data from this month's using CASE statement logic.
, max( case when la.class_ty_cd = 'COMM' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'FEES'
AND edi.Creation_dt >= convert(varchar,#Start,112) then th.trans_qty
when la.class_ty_cd = 'MUNI' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'EXCS'
AND edi.Creation_dt >= convert(varchar,#Start,112) then th.trans_qty
when la.class_ty_cd <> 'COMM' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'BASE'
AND edi.Creation_dt >= convert(varchar,#Start,112) then th.trans_qty
else 0 end) as Volume
, sum(CASE WHEN edi.Creation_dt >= convert(varchar,#Start,112) THEN th.trans_am ELSE 0 END) as Total
, max( case when la.class_ty_cd = 'COMM' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'FEES'
AND edi.Creation_dt < convert(varchar,#Start,112) then th.trans_qty
when la.class_ty_cd = 'MUNI' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'EXCS'
AND edi.Creation_dt < convert(varchar,#Start,112) then th.trans_qty
when la.class_ty_cd <> 'COMM' and th.ppa_in = 'N' and th.trans_sub_ty_cd = 'BASE'
AND edi.Creation_dt < convert(varchar,#Start,112) then th.trans_qty
else 0 end) as PrevVolume
, sum(CASE WHEN edi.Creation_dt < convert(varchar,#Start,112) THEN th.trans_am ELSE 0 END) as PrevTotal