Home  >  Q&A  >  body text

Is there a way to display only the first non-zero value, but if all values ​​are zero, display the first instance of a column in a separate table from the joined table

The two tables are quite large and my select statement has more values ​​than I get, but I think I can simplify this data and query in order to answer my question.

This 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. Invoice table and InvoiceItem table. They are joined by the InvoiceNum column in each table and display the InvoiceNum and Layer columns

The following are the results of 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 results in duplicate InvoiceNums in my results, which I don't want.

This is the result I am trying to get, to list only 1 invoice number from the invoice table, where the first case is a non-zero value from the layer column of the InvoiceItem table, and if there is no non-zero value, list The first zero.

Try something like this:

InvoiceNum  | Layer | InvoiceItemNum
1           | 10    | 1
2           | 3     | 2 
3           | 0     | 1
4           | 5     | 3

I'm just not sure how to do this, or if it's even possible given that they are on two different tables.

P粉563831052P粉563831052241 days ago325

reply all(2)I'll reply

  • P粉310931198

    P粉3109311982024-02-22 12:26:01

    Assumption/Understanding:

    • Although marked with sybase, the question does not differentiate between the 4x different Sybase RDBMS products (ASE, SQLAnywhere, IQ, Advantage), so I'll stick with common SQL syntax (i.e. 4x products have different SQL dialects; also, ASE doesn't support CTE)
    • I don't understand the OP's use of left (outer) join as the output provided does not seem to indicate that there are any "missing" rows in InvoiceItem
    • It's not clear which table the Layer and InvoiceItemNum columns belong to, so I'm assuming they belong to InvoiceItem

    Guess a set of minimal 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 few different (complex, confusing) ideas for generating the output the OP needs:

    -- 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 will generate:

    InvoiceNum  Layer       InvoiceItemNum
     ----------- ----------- --------------
               1          10              1
               2           3              2
               3           0              1
               4           5              3

    Comments:

    • Not sure what the output should be since it doesn't appear (for me) and the OP has demonstrated the need for a left (external) join
    • All queries tested in (Sybase)SAP ASE 16.0

    reply
    0
  • P粉551084295

    P粉5510842952024-02-22 10:50:28

    This question is a bit tricky:

    Try it 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

    Demo

    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
    )

    reply
    0
  • Cancelreply