Search code examples
pythonjoinsqlalchemy

Joining an aliased selectable in Sqlalchemy Core


I am attempting to make a left join on two tables: invoices and vendors. The typical problem is that I have multiple entries in the right table (vendors) which leads to duplicate results:

    Vendors             Invoices    
    vend_id name        vend_id  line_amt
     001    Lowes       001      5.95
     001    lowes       002      17
     001    Lowes_ca    002      25
     002    Bills       002      40
     002    Bill's      003      4.35
     003    Two Alphas  003      3.75
     004    Apple Cartz 003      10
                        004      23
                        004      56
                        004      80

I'm looking for this:

Desired Result:     
vend_id   line_amt      name
001       5.95          Lowes
002       17            Bills
002       25            Bills
002       40            Bills
003       4.35          Two Alphas
003       3.75          Two Alphas
003       10            Two Alphas
004       23            Apple Cartz
004       56            Apple Cartz
004       80            Apple Cartz

But I am getting this:

vend_id   line_amt      name
001       5.95          Lowes
001       5.95          lowes
001       5.95          Lowes_ca
002       17            Bills
002       17            Bill's
002       25            Bills
002       25            Bill's
002       40            Bills
002       40            Bill's
003       4.35          Two Alphas
003       3.75          Two Alphas
003       10            Two Alphas
004       23            Apple Cartz
004       56            Apple Cartz
004       80            Apple Cartz

So I'm trying the code below to join on a selectable in sqlalchemy core, but I am getting a Not an executable clause error. I can't use the ORM because of the way the db is set up. Is there a way to alter this code or a better solution that I am not thinking of?

conn = engine.connect()

a = select([vendors.c.vend_id.label('vend_id'),
func.min(vendors.c.name).label('name')]).group_by(vendors.c.vend_id).alias('a')

s = select([
        invoices.c.vend_id.label('vendor'),
        invoices.c.line_amt.label('amount'),
        ]).join(a, a.c.vend_id == invoices.c.vend_id)

p = conn.execute(s)

Solution

  • First joining the invoices table to the aliased table will work. I needed to use .select_from in order to complete the join. This is the code that works:

        conn = engine.connect()
    
        a = select([vendors.c.vend_id.label('vend_id'),
        func.min(vendors.c.name).label('name')]).group_by(vendors.c.vend_id).alias('a')
    
        j = invoices.join(a, a.c.vend_id == invoices.c.vend_id)
    
        s = select([
                invoices.c.vend_id.label('vendor'),
                a.c.name.label('name'),
                invoices.c.line_amt.label('amount'),
                ]).select_from(j)
    
        p = conn.execute(s)