(Beginner) SQLAlchemy JOIN with SQLITE - python

I am studying sqlalchemy and I cannot understand the reason for this error.
from sqlalchemy import create_engine, Table, Column, MetaData, INTEGER, String, ForeignKey, ForeignKeyConstraint
from sqlalchemy.sql import Select
engine = create_engine('sqlite:///:memory:')
with engine.connect() as conn:
meta = MetaData(engine)
cars = Table('Cars', meta,
Column('Id', INTEGER, primary_key=True, autoincrement=True),
Column('Name', String, nullable=False),
Column('BrandId', INTEGER, ForeignKey('Brands.Id')))
brands = Table('Brands', meta,
Column('Id', INTEGER, primary_key=True, autoincrement=True),
Column('Name', String))
meta.create_all()
cars_values = [{'Name':'Escort', 'BrandId':1}]
brands_values = [{'Id':1, 'Name': 'Ford'}]
insert1 = brands.insert().values(brands_values)
insert2 = cars.insert().values(cars_values)
conn.execute(insert1)
conn.execute(insert2)
query = Select([cars]).join(brands, brands.c.Id == cars.c.BrandId)
#query = 'select * from cars c JOIN brands b on b.id = c.brandid'
result = conn.execute(query)
print(result.fetchall())
When I run this way, I get an error
Select([cars]).join(brands, brands.c.Id == cars.c.BrandId)
sqlalchemy.exc.ObjectNotExecutableError: Not an executable object: <sqlalchemy.sql.selectable.Join at 0x1e62654ed88; Join object on Select object(2087997204936) and Brands(2087997203848)>
But if you run raw sql the JOIN is accepted
'select * from cars c JOIN brands b on b.id = c.brandid'
[(1, 'Escort', 1, 1, 'Ford')]

query = select(['*']).select_from(cars.join(brands, brands.c.Id == cars.c.BrandId))
# print(query)
# SELECT * FROM "Cars" JOIN "Brands" ON "Brands"."Id" = "Cars"."BrandId"
see Using Joins

This is another way
from sqlalchemy import text
query = text("select c.Id, c.Name,c.BrandId, b.Id, b.Name from Cars c left join
Brands b on b.Id = b.Id ")
result = engine.execute(query)

Related

How to set alias in the sqlalchemy for the table?

I'm trying to write MySQL query in the sqlalchemy syntax, but I don't know how to represent aliasForC. Could you help me?
Query with aliasForC alias:
SELECT aliasForC.secondId
FROM A, B, C as aliasForC
WHERE B.firstId = A.firstId
AND B.status = 'Reprep'
AND A.secondId = aliasForC.secondId
AND B.status = ALL (
SELECT status
FROM C
INNER JOIN A ON A.secondId = C.secondId
INNER JOIN B ON A.firstId = B.firstId
WHERE code = aliasForC.code
)
You can do it in this way:
aliasForC = aliased(C)
# And then:
join(aliasForC, aliasForC.firstId == A.firstId )
For All statement, you can use all_()
I think alias is what you're looking for.
http://docs.sqlalchemy.org/en/latest/core/selectable.html
http://docs.sqlalchemy.org/en/latest/core/selectable.html#sqlalchemy.sql.expression.Alias
user_alias = aliased(User, name='user2')
q = sess.query(User, User.id, user_alias)
See: http://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.orm.query.Query.column_descriptions
import sqlparse
import sqlalchemy as sa
meta = sa.MetaData()
a = sa.Table(
'a', meta,
sa.Column('id', sa.Integer, primary_key=True),
)
b = sa.Table(
'b', meta,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('x', sa.Integer, sa.ForeignKey(a.c.id)),
sa.Column('y', sa.Integer, sa.ForeignKey(a.c.id)),
)
x = b.alias('x')
y = b.alias('y')
query = (
sa.select(['*']).
select_from(a.join(x, a.c.id == x.c.x)).
select_from(a.join(y, a.c.id == y.c.y))
)
print(sqlparse.format(str(query), reindent=True))
# OUTPUT:
#
# SELECT *
# FROM a
# JOIN b AS x ON a.id = x.x,
# a
# JOIN b AS y ON a.id = y.y
Per https://gist.github.com/sirex/04ed17b9c9d61482f98b#file-main-py-L27-L28

How to properly join same table multiple times using sqlalchemy core api?

I'm trying to join same table several times, using sqlalchemy core api.
Here is the code:
import sqlparse
import sqlalchemy as sa
meta = sa.MetaData('sqlite:///:memory:')
a = sa.Table(
'a', meta,
sa.Column('id', sa.Integer, primary_key=True),
)
b = sa.Table(
'b', meta,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('x', sa.Integer, sa.ForeignKey(a.c.id)),
sa.Column('y', sa.Integer, sa.ForeignKey(a.c.id)),
)
meta.create_all()
x = b.alias('x')
y = b.alias('y')
query = (
sa.select(['*']).
select_from(a.join(x, a.c.id == x.c.x)).
select_from(a.join(y, a.c.id == y.c.y))
)
print(sqlparse.format(str(query), reindent=True))
Last statement produces following output:
SELECT *
FROM a
JOIN b AS x ON a.id = x.x,
a
JOIN b AS y ON a.id = y.y
If I try to execute this query query.execute() I get error:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) ambiguous column name: main.a.id [SQL: 'SELECT * \nFROM a JOIN b AS x ON a.id = x.x, a JOIN b AS y ON a.id = y.y']
The question is, how can I get rid of , a? If I try to execute:
engine.execute('''
SELECT *
FROM a
JOIN b AS x ON a.id = x.x
JOIN b AS y ON a.id = y.y
''')
It works fine.
query = (
sa.select(['*']).
select_from(a
.join(x, a.c.id == x.c.x)
.join(y, a.c.id == y.c.y)
)
)

Multiple joins in a SELECT

I have three tables. These are joined by ForeignKey constraints so sqlalchemy knows how to join them.
I want to select the columns from all three tables:
select([a.c.x, b.c.x, c.c.x], a.c.a.between(10,20), [join(a, c), join(a, b)])
This generates the broken SQL:
SELECT a.x, b.x, c.x
FROM
a JOIN b ON a.b_id == b.id,
a JOIN c ON a.c_id == c.id
WHERE
a.a BETWEEN 10 AND 20;
As can be seen, the table a is in the FROM clause twice!
How can you join three tables in a select() statement using sqlalchemy?
The short answer is
select([a.c.x, b.c.x, c.c.x]).\
select_from(a.join(b).join(c)).\
where(between(a.c.a, 5, 15))
And if someone want's to try it out here's the whole thing.
import sqlalchemy
from sqlalchemy import Table, Column, Integer, String, Sequence,\
ForeignKey, select, between
meta = sqlalchemy.MetaData()
url = 'sqlite:///:memory:'
engine = sqlalchemy.create_engine(url)
a = Table(
'a', meta,
Column('id', Integer, Sequence('a_id_seq'), primary_key=True),
Column('age', Integer),
Column('name', String(20))
)
b = Table(
'b', meta,
Column('a_id', Integer, ForeignKey("a.id")),
Column('value', String(20))
)
c = Table(
'c', meta,
Column('a_id', Integer, ForeignKey("a.id")),
Column('title', String(20))
)
# Create tables
meta.create_all(engine)
# Fill with dummy data
def add_data(age, name, value, title):
q = a.insert().values({a.c.age: age, a.c.name: name})
res = engine.execute(q)
a_id = res.inserted_primary_key[0]
q = b.insert().values({b.c.a_id: a_id, b.c.value: value})
engine.execute(q)
q = c.insert().values({c.c.a_id: a_id, c.c.title: title})
engine.execute(q)
add_data(12, 'Foo', 'Bar', 'Baz')
add_data(17, '111', '222', '333')
q = select([a.c.name, b.c.value, c.c.title]).\
select_from(a.join(b).join(c)).\
where(between(a.c.age, 5, 15))
print(str(q))
# SELECT a.name, b.value, c.title
# FROM a JOIN b ON a.id = b.a_id JOIN c ON a.id = c.a_id
# WHERE a.age BETWEEN :age_1 AND :age_2
res = engine.execute(q)
for row in res.fetchall():
print(row)
# ('Foo', 'Bar', 'Baz')
Updated answer, thx for the comment Will!
give the below a go.
SELECT a.x, b.x, c.x
FROM *TABLENAME* a
JOIN *TABLENAME* b
ON a.id = b.id
JOIN *TABLENAME* c
ON a.id = c.id
WHERE
a.a BETWEEN 10 AND 20

SQLAlchemy convert outer join ORM query to Core

I'm having problems with SQLAlchemy's select_from statement when using the core component. I try to construct an outer join query which currently looks like:
query = select([b1.c.id, b1.c.num, n1.c.name, n1.c.num, ...]
).where(and_(
... some conditions ...
)
).select_from(
???.outerjoin(
n1,
and_(
... some conditions ...
)
).select_from(... more outer joins similar to the above ...)
According to the docs, the structure should look like this:
table1 = table('t1', column('a'))
table2 = table('t2', column('b'))
s = select([table1.c.a]).\
select_from(
table1.join(table2, table1.c.a==table2.c.b)
)
My problem is that I don't have a table1 object in this case, as the select ... part consists of columns and not a single table (see question marks in my query). I've tried using n1.outerjoin(n1..., but that caused an exception (Exception: (ProgrammingError) table name "n1" specified more than once).
The above snippet is derived from a working session-based (ORM) query, which I try to convert (with limited success).
b = Table('b', metadata,
Column('id', Integer, Sequence('seq_b_id')),
Column('num', Integer, nullable=False),
Column('active', Boolean, default=False),
)
n = Table('n', metadata,
Column('b_id', Integer, nullable=False),
Column('num', Integer, nullable=False),
Column('active', Boolean, default=False),
)
p = Table('p', metadata,
Column('b_id', Integer, nullable=False),
Column('num', Integer, nullable=False),
Column('active', Boolean, default=False),
)
n1 = aliased(n, name='n1')
n2 = aliased(n, name='n2')
b1 = aliased(b, name='b1')
b2 = aliased(b, name='b2')
p1 = aliased(p, name='p1')
p2 = aliased(p, name='p2')
result = sess.query(b1.id, b1.num, n1.c.name, n1.c.num, p1.par, p1.num).filter(
b1.active==False,
b1.num==sess.query(func.max(b2.num)).filter(
b2.id==b1.id
)
).outerjoin(
n1,
and_(
n1.c.b_id==b1.id,
n1.c.num<=num,
n1.c.active==False,
n1.c.num==sess.query(func.max(n2.num)).filter(
n2.id==n1.c.id
)
)
).outerjoin(
p1,
and_(
p1.b_id==b1.id,
p1.num<=num,
p1.active==False,
p1.num==sess.query(func.max(p2.num)).filter(
p2.id==p1.id
)
)
).order_by(b1.id)
How do I go about converting this ORM query into a plain Core query?
Update:
I was able to narrow down the problem. It seems that a combination of two select_from calls causes the problem.
customer = Table('customer', metadata,
Column('id', Integer),
Column('name', String(50)),
)
order = Table('order', metadata,
Column('id', Integer),
Column('customer_id', Integer),
Column('order_num', Integer),
)
address = Table('address', metadata,
Column('id', Integer),
Column('customer_id', Integer),
Column('city', String(50)),
)
metadata.create_all(db)
customer1 = aliased(customer, name='customer1')
order1 = aliased(order, name='order1')
address1 = aliased(address, name='address1')
columns = [
customer1.c.id, customer.c.name,
order1.c.id, order1.c.order_num,
address1.c.id, address1.c.city
]
query = select(columns)
query = query.select_from(
customer1.outerjoin(
order1,
and_(
order1.c.customer_id==customer1.c.id,
)
)
)
query = query.select_from(
customer1.outerjoin(
address1,
and_(
customer1.c.id==address1.c.customer_id
)
)
)
result = connection.execute(query)
for r in result.fetchall():
print r
The above code causes the following exception:
ProgrammingError: (ProgrammingError) table name "customer1" specified more than once
'SELECT customer1.id, customer.name, order1.id, order1.order_num, address1.id, address1.city \nFROM customer, customer AS customer1 LEFT OUTER JOIN "order" AS order1 ON order1.customer_id = customer1.id, customer AS customer1 LEFT OUTER JOIN address AS address1 ON customer1.id = address1.customer_id' {}
If I was a bit more experienced in using SQLAlchemy, I would say this could be a bug...
I finally managed to solved the problem. Instead of cascading select_from, additional joins need to be chained to the actual join. The above query would read:
query = select(columns)
query = query.select_from(
customer1.outerjoin(
order1,
and_(
order1.c.customer_id==customer1.c.id,
)
).outerjoin(
address1,
and_(
customer1.c.id==address1.c.customer_id
)
)
)

How to join the same table in sqlalchemy

I'm trying to join the same table in sqlalchemy. This is a minimial version of what I tried:
#!/usr/bin/env python
import sqlalchemy as sa
from sqlalchemy import create_engine
from sqlalchemy.orm import mapper, sessionmaker, aliased
engine = create_engine('sqlite:///:memory:', echo=True)
metadata = sa.MetaData()
device_table = sa.Table("device", metadata,
sa.Column("device_id", sa.Integer, primary_key=True),
sa.Column("name", sa.String(255), nullable=False),
sa.Column("parent_device_id", sa.Integer, sa.ForeignKey('device.device_id')),
)
class Device(object):
device_id = None
def __init__(self, name, parent_device_id=None):
self.name = name
self.parent_device_id = parent_device_id
def __repr__(self):
return "<Device(%s, '%s', %s)>" % (self.device_id,
self.name,
self.parent_device_id )
mapper(Device, device_table)
metadata.create_all(engine)
db_session = sessionmaker(bind=engine)()
parent = Device('parent')
db_session.add(parent)
db_session.commit()
child = Device('child', parent.device_id)
db_session.add(child)
db_session.commit()
ParentDevice = aliased(Device, name='parent_device')
q = db_session.query(Device, ParentDevice)\
.outerjoin(ParentDevice,
Device.parent_device_id==ParentDevice.device_id)
print list(q)
This gives me this error:
ArgumentError: Can't determine join between 'device' and 'parent_device'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.
But I am specifying a onclause for the join. How should I be doing this?
For query.[outer]join, you specify as list of joins (which is different to expression.[outer]join.) So I needed to put the 2 elements of the join, the table and the onclause in a tuple, like this:
q = db_session.query(Device, ParentDevice)\
.outerjoin(
(ParentDevice, Device.parent_device_id==ParentDevice.device_id)
)
Your mapper should specificy the connection between the two items, here's an example: adjacency list relationships.

Categories