Execute a double inner join in sqlalchemy - mysql

I have this SQL-Query, which I would like to turn into sqlalchemy code.
I'm working here with an already existing database in production which makes it difficult to change.
SELECT B.IDTestung, B.Ber1_Konzentration,
T.Testungstyp, T.Timestamp,
K.Name_Kl, K.Vorname_Kl
FROM BCRS AS B
INNER JOIN
ttestungen AS T on B.IDTestung = T.IDTESTUNG
INNER JOIN
tklienten K on T.IDKLIENT = K.IDKLIENT
So, basically there's the table BCRS, which contains an ID for the "Testung" and the table ttestungen itself has an ID to identify the client. So there are two inner joins to combine three tables.
How do I write this in sqlalchemy?
What I got so far:
from auswertungen.data.data import Tklienten, BCR, Ttestungen
tests = session.query(BCR).join(Ttestungen.tklienten).all()
This makes the first join, but I don't know how to make the second. I tried to just add another .join(...) after the first one, but that didn't work.
Here's is the DB-Definitions (excerpt):
(automatically created with sqlacodegen)
class BCR(Base):
__tablename__ = 'BCRS'
IDBCRS_Testung = Column(INTEGER(11), primary_key=True)
IDTestung = Column(ForeignKey('ttestungen.IDTESTUNG', ondelete='CASCADE'), unique=True)
[...]
ttestungen = relationship('Ttestungen')
class Ttestungen(Base):
__tablename__ = 'ttestungen'
IDTESTUNG = Column(INTEGER(10), primary_key=True)
IDKLIENT = Column(ForeignKey('tklienten.IDKLIENT'), index=True)
[...]
tklienten = relationship('Tklienten')

You can try this below:
test = session.query(BCR).join(Ttestungen, BCR.IDTestung == Ttestungen.IDTESTUNG).join(Tklienten, Ttestungen.IDKLIENT == Tklienten.IDKLIENT).all()

Related

SqlAlchemy - make a query where Relationship Attribute is a list

I have two models:
class Profile(Base):
__tablename__ = 'profiles'
id = Column(Integer, primary_key=True)
...
stagesP_list = relationship(
'StageP',
back_populates='profiles_list',
secondary=stageP_profile
)
class Project(Base):
__tablename__ = 'projects'
id = Column(Integer, primary_key=True)
...
stagesP_list = relationship(
'StageP',
back_populates='projects_list',
secondary=stageP_project
)
I need to select Profiles for which at least one value of the Profile.stagesP_list is contained in the project.stagesP_list.
Please help to compose the query or indicate the direction in which to search.
If you have project instance loaded, you can compose the following query:
project = ...
stageP_ids = [obj.id for obj in project.stagesP_list]
query = session.query(Profile).filter(
Profile.stagesP_list.any(StageP.id.in_(stageP_ids))
)
You can also perform joins on the database directly from having only project_id:
query = (
session.query(Profile)
.join(StageP, Profile.stagesP_list)
.join(Project, StageP.projects_list)
.where(Project.id == project_id)
.distinct()
)

How to make this query in sqlalchemy?

SELECT
maintener.*,
(SELECT COUNT(*)
FROM device d
WHERE d.in_stock_maintener_id = maintener.id) AS in_stock_devices
FROM maintener;
I'm creating a report that show all mainteners but i need to show the number of devices that each one of that mainteners has by looking at the devices model reference in_stock_maintener_id;
I have this models in my persist sqlalchemy.
class Maintener(persist.Base):
__tablename__ = 'maintener'
id = Column(Integer, primary_key=True)
name = Column(String(255))
document_number = Column(String(30))
phone_1 = Column(String(12))
phone_2 = Column(String(12))
email = Column(String(255))
class Device(persist.Base):
__tablename__ = 'device'
id = Column(Integer, primary_key=True)
serial = Column(String(45))
in_stock = Column(SmallInteger)
in_stock_maintener_id = Column(ForeignKey(u'maintener.id'), nullable=True, index=True)
in_stock_maintener = relationship(u'Maintener', lazy='noload', \
primaryjoin='Device.in_stock_maintener_id == Maintener.id')
If anyone could help me, i'll be grateful =)
sq = (
session
.query(func.count())
.select_from(Device)
.filter(Device.in_stock_maintener_id == Maintener.id)
).as_scalar()
q = session.query(Maintener, sq.label('in_stock_devices'))
Query above will return an enumerable of tuple(Maintener, Integer).
If you would like to have columns instead (as per your comment), then you can either specify the columns you want in the query implicitly:
q = session.query(Maintener.id, Maintener.name, sq.label('in_stock_devices'))
or if you would like all columns (as in SELECT *), then you could query the Table instead of the mapped entity:
q = session.query(Maintener.__table__, sq.label('in_stock_devices'))
Above I assumed that you use declarative extension.

SQLAlchemy: how to recreate the table

In SQLAlchemy, how to recreate the table. first, I create a table ToDoTask, with 4 columns: content, id, priority and status by the following code:
Base = declarative_base()
class ToDoTask(Base):
__tablename__ = 'todotask'
content = Column(String(250), nullable = False)
id = Column(Integer, primary_key = True)
priority = Column(Integer)
status = Column(String(8))
engine = create_engine('sqlite:///todotask.db')
Base.metadata.create_all(engine)
then if I found I need to redesign the table by add a new column,for example Time. I add the column into the Class, and rerun, but checked with inspect function and found the Time column is not added.
so how to do this?
If I get it right, I think you should use a migration tool like Alembic

Usage of a COUNT(DISTINCT field) with a GROUP BY clause in Django

Problem
I want to use a COUNT(DISTINCT field) with a GROUP BY clause in Django. As I understand, the COUNT(DISTINCT... can only be achieved by using an extra for the query set.
My simplified model is :
class Site(models.Model):
name = models.CharField(max_length=128, unique=True)
class Application(models.Model):
name = models.CharField(max_length=64)
version = models.CharField(max_length=13, db_index=True)
class User(models.Model):
name = models.CharField(max_length=64)
site = models.ForeignKey(Site, db_index=True)
class Device(models.Model):
imei = models.CharField(max_length=16, unique=True)
applications = models.ManyToManyField(Application, null=True, db_index=True, through='ApplicationUsage')
user = models.ForeignKey(User, null=True, db_index=True)
class ApplicationUsage(models.Model):
activity = models.DateField(db_index=True)
application = models.ForeignKey(Application)
device = models.ForeignKey(Device)
My goal is to have a liste of Site objects with a count of distinct device for each site given an application activity through a time period, something like
stats_site.name deviceCount
ALBI 32
AMPLEPUIS 42
...
I try this code :
qs = models.Site.objects.filter(user__device__applicationusage__activity__range=[startDay, endDay])\
.extra(select={'deviceCount' : 'COUNT(DISTINCT `stats_device`.`id`)'})\
.values('name', 'deviceCount')\
The generated SQL is :
SELECT (COUNT(DISTINCT stats_device.id)) AS deviceCount, stats_site.name
FROM stats_site
INNER JOIN stats_user ON (stats_site.id = stats_user.site_id)
INNER JOIN stats_device ON (stats_user.id = stats_device.user_id)
INNER JOIN stats_applicationusage ON (stats_device.id = stats_applicationusage.device_id)
WHERE stats_applicationusage.activity BETWEEN '2013-07-01' AND '2013-07-03'
And the result is obviously wrong since it lacks the GROUP BY clause, which should be GROUP BY stats_site.name
The problem is: I don't know how to add the correct GROUP BY using the annotate function or other.
Solution
Using distinct=True on the Count function with annotate:
qs = models.Site.objects.filter(habileouser__device__applicationusage__activity__range=[startDay, endDay])\
.annotate(deviceCount=Count('habileouser__device', distinct=True))\
.values('name', 'deviceCount')
The annotate method of a queryset will calculate an aggregate value for each element of the queryset, and when used after a values call will aggregate over the values of the values. I think this should work:
qs = models.Site.objects.filter(
user__device__applicationusage__activity__range=[startDay, endDay]
).values('name').annotate(Count('user__device', distinct=True))
If you have an ordering specified you may need to remove it as discussed here:
https://docs.djangoproject.com/en/dev/topics/db/aggregation/#interaction-with-default-ordering-or-order-by

SQLAlchemy error - "Please configure one or more attributes for these same-named columns explicitly."

I am brand new to sqlalchemy. Trying to get a query to work and am having issues with a join.
I have two tables both of which have a column named "Id" and I need to join on that table. My code looks like this:
table1 = server.tab1
table2 = server.tab2
joined = server.join(table1,table2, table1.Id == table2.Id)
where = table1.createDate > start
results = joined.filter(where).all()
This results in the following error message:
Implicitly combining column table1.Id with column table2.Id under attribute 'Id'. Please configure one or more attributes for these same-named columns explicitly.
Question is, how do I configure these attributes?
TIA!
With sql soup
joined = server.session.query(table1).join((table2,table1.id == table2.id))
where = table1.createDate > start
results = joined.filter(where).all()
I had this same issue so I figured I would add the solution I came up with (based on http://www.mail-archive.com/sqlalchemy#googlegroups.com/msg23735.html). It's certainly not the cleanest thing I've ever coded, but using your example from above, it would be approximately:
from sqlalchemy import select
aliased_table1 = select([
table1.c.Id.label("renamed_id_col"),
table1.c.any_other_columns_you_want_returned_but_not_renamed,
...
]).correlate(None).alias()
joined = server.join(aliased_table1, table2, aliased_table1.c.renamed_id_col == table2.Id)
where = aliased_table1.c.createDate > start
results = joined.filter(where).all()
One way about this is to label all the columns in one of the tables, so that you don't have any collision of the column names:
table1 = server.tab1
table2 = server.with_labels(server.tab2)
joined = server.join(table1,table2, table1.Id == table2.tab2_Id)
where = table1.createDate > start
results = joined.filter(where).all()
table2 ends up being a labeled table, where all the column names are prefaced with the table name, so as not to interfere with the column names in table1.
You can use join feature provided by sqlalchemy,
see the example below, no need to do it manually sqlalchme does it for us,
from sqlalchemy import create_engine, Column, String, Integer, Table, ForeignKey
from sqlalchemy.orm import mapper, relationship
from sqlalchemy.schema import MetaData
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column('user_id',Integer,primary_key = True)
name = Column('user_name',String(20))
addresses = relationship("Address",backref="user")
class Address(Base):
__tablename__ = 'addresses'
id = Column('user_id',ForeignKey('users.user_id'))
address = Column('address',String(30))
pk = Column('address_id',Integer,primary_key=1)
if __name__ == "__main__":
engine = create_engine("sqlite://", echo = True)
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()
u1 = User(name='japan')
session.add(u1)
session.commit()
u1.addresses.append(Address(address='a1'))
u1.addresses.append(Address(address='a2'))
session.flush()
q = session.query(User).join(User.addresses).all()
print "="*23,q