On 03 Apr 2013 12:15, Stone, Joel wrote:
<<SNIP>>

Now I have to roll up THREE levels; Hdr, DTL, and sub-DTL.

I thought that I could use the join ideas presented here, but it
seems that the SUM() functions duplicate rows in three levels and
the sums are multiples of what is required.

Can any of the examples using TWO levels (HDR & DTL) be expounded
upon to add a third level?

Or is there a better way to make this happen?

I like things simple - is there a way to accomplish this in steps
so it is simple and I can understand what I did a month from now?

Given the Hdr file is defined as:

create table OHdr
( ordno dec(7), primary key (ordno), TotQty dec(17)
)
;

Does the DDL for the other two files look something like?:

create table ODtl
( ordno dec(7)
, DtlQty dec(13)
, foreign key (ordno) references OHdr (ordno)
on delete cascade on update restrict
) /* and SUM(DtlQty) should match the TotQty in Hdr file */
;

create table OSub
( ordno dec(7)
, SubQty dec(13)
, foreign key (ordno) references OHdr (ordno)
on delete cascade on update restrict
) /* and SUM(SubQty) should match the TotQty in Hdr file */
;

If so [noting the assumption in the comment that the summed values of SubQty should equate with TotQty, just as the summed values of DtlQty should equate with TotQty], then...

I would probably choose to run the join query of the two files [Hdr and Dtl files], and then repeat that query, but using the sub-Dtl file in place of the Dtl file. Comparing the TotQty column value to just one other column is much clearer than comparing to two other columns, esp. because there could be at least one correctly matching. Regardless, ...

IMO the *simplest* means which would have the future readers of the SQL being the least confused, is to encapsulate each of the summary queries in their own VIEW, *then* join the header file with the summary VIEWs in a SELECT. IMO this is also easier for the initial creation, because the steps are incremental work and each step is straightforward, and allows the final query request to be very succinct:

create view ODtlSum as
( select ordno, dec( sum(DtlQty), 17 ) as SumDtlQty
from ODtl
group by ordno
) /* One row per ordno gives one-to-one or one-to-zero */
;

create view OSubSum as
( select ordno, dec( sum(SubQty), 17 ) as SumSubQty
from ODtl
group by ordno
) /* One row per ordno gives one-to-one or one-to-zero */
;

select H.ordno, H.TotQty, D.SumDtlQty, S.SumSubQty
from OHdr H
/* left or inner */ join ODtlSum D
using (ordno)
/* left or inner */ join OSubSum S
using (ordno)
where H.TotQty <> D.SumDtlQty
or H.TotQty <> S.SumSubQty

After the CREATE VIEW activity is completed as setup activity, the eventual SELECT that is coded is very simple and is IMO also very clear. The definitions of the VIEWs are easily reviewed from the catalog, DSPFD, or other retrieval [e.g. by the retrieve SQL DDL API or iNav using that API].


Avoiding the separate VIEW objects to manage, the SELECT can get data from the summary results [that like the VIEWs also give one-to-one or one-to-zero for ordno when joined with Hdr] in a derived-table or subquery, and the join performed with the header to those temporary results; e.g. perhaps one of:

/* join using a NTE for each summary query */
select H.ordno, H.TotQty, D.SumDtlQty, S.SumSubQty
from OHdr H
/* left or inner */ join
( select ordno, dec( sum(DtlQty), 17 ) as SumDtlQty
from ODtl
group by ordno ) D
on H.ordno = D.ordno
/* left or inner */ join
( select ordno, dec( sum(SubQty), 17 ) as SumSubQty
from OSub
group by ordno ) S
on D.ordno = S.ordno
where H.TotQty <> D.SumDtlQty
or H.TotQty <> S.SumSubQty


/* above is easily modified to use a CTE vs NTE for agggregates */
with
SumDtl as
( select ordno, dec( sum(DtlQty), 17 ) as SumDtlQty
from ODtl
group by ordno
)
, SumSub as
( select ordno, dec( sum(SubQty), 17 ) as SumSubQty
from OSub
group by ordno
)
select H.ordno, H.TotQty, D.SumDtlQty, S.SumSubQty
from OHdr H
/* left or inner */ join
SumDtl D
on H.ordno = D.ordno
/* left or inner */ join
SumSub S
on D.ordno = S.ordno
where H.TotQty <> D.SumDtlQty
or H.TotQty <> S.SumSubQty


/* an implied join using correlated scalar subselects */
select H.ordno, H.TotQty
, ( select dec( sum(DtlQty), 17 )
from ODtl D
where D.ordno = H.ordno
) as SumDtlQty
, ( select dec( sum(SubQty), 17 )
from OSub S
where S.ordno = H.ordno
) as SumSubQty
from OHdr H
where H.TotQty <> ( select dec( sum(DtlQty), 17 )
from ODtl D
where D.ordno = H.ordno )
or H.TotQty <> ( select dec( sum(SubQty), 17 )
from OSub S
where S.ordno = H.ordno )


The simple grouping query with the SUM aggregate using HAVING selection for selection, a query that was offered as a resolution in the prior message thread [subject: SQL: how to join header and detail records] /breaks/ when trying to add another file to the JOIN because the number of rows multiplies. A summation of the values for each joined table is off by a multiple of the counts of rows added by each additional joined table. While there are presumably ways to accomplish such a query, they would be ugly. For example the following [albeit this fails due to sql0112 on v5r3, which may or may not be a proper error denoting a restriction which precludes that attempt]:

select h.ordno, h.totqty
, dec( dec(sum(dtlqty), 17)
/ (select int(count(*))
from osub s
where s.ordno = h.ordno )
, 17
) as dtlsum
, dec( dec(sum(subqty), 17)
/ (select int(count(*))
from odtl d
where d.ordno = h.ordno )
, 17
) as subsum
from ohdr h
join odtl d
on h.ordno = d.ordno
join osub s
on d.ordno = s.ordno
group by h.ordno , h.totqty
having h.totqty <> dec( dec(sum(dtlqty), 17)
/ (select int(count(*))
from osub s
where s.ordno = h.ordno )
, 17)
or h.totqty <> dec( dec(sum(subqty), 17)
/ (select int(count(*))
from odtl d
where d.ordno = h.ordno )
, 17)



As an Amazon Associate we earn from qualifying purchases.

This thread ...

Follow-Ups:
Replies:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.