These two tables are quite large, and my select statement has more values I am obtaining, but I think I can simplify this data and query so my question can be answered.
Here is my select statement:
SELECT invoice.InvoiceNum, Layer, InvoiceItemNum
FROM (INVOICE
left outer join InvoiceItem item
ON item.InvoiceNum = Invoice.InvoiceNum
)
ORDER BY invoice.InvoiceNum
So I have two tables. An Invoice table and an InvoiceItem table. They are joined by the InvoiceNum column in each table, and displaying the InvoiceNum and Layer Column
Here is a result to this query:
InvoiceNum | Layer | InvoiceItemNum
1 | 10 | 1
1 | 0 | 2
1 | 7 | 3
1 | 0 | 4
2 | 0 | 1
2 | 3 | 2
3 | 0 | 1
3 | 0 | 2
3 | 0 | 3
4 | 0 | 1
4 | 0 | 2
4 | 5 | 3
Since my InvoiceItem table has multiple rows that can be assigned to 1 InvoiceNum this is causing me to have duplicate invoiceNums in my result, which I do not want.
Here is the result I am trying to get, only listing 1 invoiceNum from the Invoice table, with the first case of a non-zero value from the InvoiceItem table's layer column, and If there is no non-zero than list the first zero.
Trying for something like this:
InvoiceNum | Layer | InvoiceItemNum
1 | 10 | 1
2 | 3 | 2
3 | 0 | 1
4 | 5 | 3
I am just not sure how to do this or if this is possible given that these are on two different tables.
CodePudding user response:
this question is a bit tricky:
Try this in Postgres:
with cte as (
select
inv.invoicenum,sum(layer::int) "sum_layer"
from invoice inv inner join invoiceitem item on item.invoicenum=inv.invoicenum
group by 1
)
,
cte1 as (
select distinct on (inv.invoicenum) inv.invoicenum,layer, InvoiceItemNum
from invoice inv inner join invoiceitem item on item.invoicenum=inv.invoicenum
where inv.invoicenum in (select invoicenum from cte where sum_layer=0)
order by inv.invoicenum, InvoiceItemNum
),
cte2 as (
select
distinct on (inv.invoicenum) inv.invoicenum, layer , InvoiceItemNum
from invoice inv inner join invoiceitem item on item.invoicenum=inv.invoicenum
where inv.invoicenum in (select invoicenum from cte where sum_layer>0) and layer::int>0
order by inv.invoicenum, InvoiceItemNum
)
(
select * from cte1
union all
select * from cte2
)
order by 1
In MySQL 8:
Try This:
with cte as (
select
inv.invoicenum,sum(layer) "sum_layer"
from invoice inv inner join invoiceitem item on item.invoicenum=inv.invoicenum
group by 1
)
,
cte1 as (
select * from (
select inv.invoicenum, layer, InvoiceItemNum
from invoice inv inner join invoiceitem item on item.invoicenum=inv.invoicenum
where inv.invoicenum in (select invoicenum from cte where sum_layer=0)
order by inv.invoicenum, InvoiceItemNum
) c
group by invoicenum
),
cte2 as (
select * from (
select inv.invoicenum, layer, InvoiceItemNum
from invoice inv inner join invoiceitem item on item.invoicenum=inv.invoicenum
where inv.invoicenum in (select invoicenum from cte where sum_layer>0) and layer>0
order by inv.invoicenum, InvoiceItemNum ) c
group by invoicenum
)
(
select * from cte1
union all
select * from cte2
)
CodePudding user response:
Assumptions/Understandings:
- while tagged with
sybase
the question does not distinguish between the 4x different Sybase RDBMS products (ASE
,SQLAnywhere
,IQ
,Advantage
) so I'm going to stick with what should be general SQL syntax (ie, the 4x products have different SQL dialects; also, ASE does not support CTEs) - I don't understand OP's use of
left (outer) join
since the provided output doesn't appear to indicate any 'missing' rows fromInvoiceItem
- it's not clear which table the
Layer
andInvoiceItemNum
columns belong to so I'm going to assume they belong toInvoiceItem
Guessing at a set of minimum table definitions and associated insert
statements:
create table Invoice
(InvoiceNum int
)
create table InvoiceItem
(InvoiceNum int
,InvoiceItemNum int
,Layer int
)
insert Invoice select 1 union all select 2 union all select 3 union all select 4
insert InvoiceItem values (1,1,10)
insert InvoiceItem values (1,2,0)
insert InvoiceItem values (1,3,7)
insert InvoiceItem values (1,4,0)
insert InvoiceItem values (2,1,0)
insert InvoiceItem values (2,2,3)
insert InvoiceItem values (3,1,0)
insert InvoiceItem values (3,2,0)
insert InvoiceItem values (3,3,0)
insert InvoiceItem values (4,1,0)
insert InvoiceItem values (4,2,0)
insert InvoiceItem values (4,3,5)
Query that generates OP's current output:
select inv.InvoiceNum,
item.Layer,
item.InvoiceItemNum
from Invoice inv
left -- superfluous in this case?
join InvoiceItem item
on inv.InvoiceNum = item.InvoiceNum
order by 1,3
InvoiceNum Layer InvoiceItemNum
----------- ----------- --------------
1 10 1
1 0 2
1 7 3
1 0 4
2 0 1
2 3 2
3 0 1
3 0 2
3 0 3
4 0 1
4 0 2
4 5 3
A couple different (convolued, messy) ideas for generating OP's desired output:
-- join based on Layer!=0; if no rows found then override NULLs
-- with Layer=0 and InvoiceItemNum=min(InvoiceItemNum) where Layer=0;
-- needs more work in case there are no matching rows in InvoiceItem ...
-- wrap case/then in a coalesce() and set to, what, 0?
select inv.InvoiceNum,
coalesce(item1.Layer,0) as "Layer",
case when item1.InvoiceItemNum is NULL
then (select min(InvoiceItemNum) from InvoiceItem item3 where item3.InvoiceNum = inv.InvoiceNum)
else item1.InvoiceItemNum
end as "InvoiceItemNum"
from Invoice inv
left
join InvoiceItem item1
on inv.InvoiceNum = item1.InvoiceNum
and item1.Layer != 0
and not exists(select 1
from InvoiceItem item2
where item2.InvoiceNum = item1.InvoiceNum
and item2.Layer != 0
and item2.InvoiceItemNum < item1.InvoiceItemNum)
order by 1
-- OR
-- perform a mutually exclusive UNION of Layer!=0 and Layer=0 queries
-- with Layer!=0 having prcedence
select inv.InvoiceNum,
item1.Layer,
item1.InvoiceItemNum
from Invoice inv
--left ??? needs work if this is really an outer join ???
join InvoiceItem item1
on inv.InvoiceNum = item1.InvoiceNum
and (
( item1.Layer != 0
and not exists(select 1
from InvoiceItem item2
where item2.InvoiceNum = item1.InvoiceNum
and item2.Layer != 0
and item2.InvoiceItemNum < item1.InvoiceItemNum)
)
or
( item1.Layer = 0
and not exists(select 1
from InvoiceItem item3
where item3.InvoiceNum = item1.InvoiceNum
and item3.Layer != 0)
and not exists(select 1
from InvoiceItem item4
where item4.InvoiceNum = item1.InvoiceNum
and item4.Layer = 0
and item4.InvoiceItemNum < item1.InvoiceItemNum)
)
)
order by 1
Both of these generate:
InvoiceNum Layer InvoiceItemNum
----------- ----------- --------------
1 10 1
2 3 2
3 0 1
4 5 3
NOTES:
- not sure what the output should be since it doesn't appear (to me) OP has demonstrated the need for the
left (outer) join
- all queries tested in (Sybase)SAP
ASE 16.0
CodePudding user response:
have you tried ASC and DESC?
SELECT invoice.InvoiceNum, Layer, InvoiceItemNum
FROM (INVOICE
left outer join InvoiceItem item
ON item.InvoiceNum = Invoice.InvoiceNum
)
ORDER BY Layer DESC, invoice.InvoiceNum ASC