× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.



Thank you so much - you went above and beyond with this one.

This is like a semester of SQL - all in one email.

Its going to take me some time to play with but I think it is what I am looking for.

I don't think the join approach works.

The files I am using are harder to explain so I used order hdr & dtl.

The real files are 4 levels: Contract, contract-terms, contract/shipment, and shipment.

The problem seems to be that for contract 123, there can be two terms 123A and 123B. For each of those there can be multiple shipments. When I join them all together, the amounts in the top levels are duplicated. When I add the joined rows together, the amounts in the TERMS level are doubled, tripped and more because of the 1-to-many relationships.

I was hoping for a simple solution but from the varied responses and especially yours, its not as simple as I had hoped - but still doable.

I was wondering why all of the SQL/400 books I have avoided these types of queries. I have two SQL books. One has only a paragraph on CTEs and the other only a page.

I think one could write an entire book on CTEs and where to use them.






-----Original Message-----
From: midrange-l-bounces@xxxxxxxxxxxx [mailto:midrange-l-bounces@xxxxxxxxxxxx] On Behalf Of CRPence
Sent: Wednesday, April 03, 2013 6:57 PM
To: midrange-l@xxxxxxxxxxxx
Subject: Re: SQL: how to join and roll up THREE levels

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.