简体   繁体   中英

SQL List items that appears less than N times in another table

Here is my database model.

class Doctor(db.Model):
    __tablename__ = 'doctor'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(120), nullable=False)

class Patient(db.Model):
    __tablename__ = 'patient'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(120), nullable=False)

class Assignment(db.Model):
    __tablename__ = 'assignment'
    doctorid = db.Column(db.Integer, ForeignKey('doctor.id'),
                        unique=False, nullable=False, index=True, primary_key=True)
    patientid = db.Column(db.Integer, ForeignKey('patient.id'),
                       unique=False, nullable=False, index=True, primary_key=True)
    created = db.Column(db.DateTime(timezone=False),
                        server_default=db.func.now())

I would like to list all Doctors who has less than 3 (may be a user-specified N ) Patients assigned. The assignment is recorded in the assignment table using ForeignKey.

Note that some Doctors may have zero Patient assigned, they should also be included in the results.

I was thinking about something like sub-queries, GROUP BY or OUTER JOIN but I couldn't figure out how to put counting N in them.

Also, I would like to further exclude all Doctors who are assigned with a certain Patient patientid = P1 from the results. Is this possible to be done in addition to the first (less then N ) condition?

To count the number of assignments per doctor use GROUP BY and to select only groups, in this case doctors, meeting certain criteria use the HAVING clause. It is possible to deterministically select non aggregate columns from a group, if they are functionally dependent on the grouping, which is the case if grouping by primary key.

docs = db.session.query(Doctor).\
    outerjoin(Assignment).\
    group_by(Doctor.id).\
    having(
        db.func.count() < n,
        db.func.count().filter(Assignment.patientid == p1) == 0).\
    all()

If your DBMS does not support the aggregate FILTER clause used above to eliminate doctors assigned with patient P1, you can use an EXISTS subquery expression instead:

docs = db.session.query(Doctor).\
    outerjoin(Assignment).\
    filter(db.not_(db.session.query(Assignment).filter(
        Assignment.patientid == p1,
        Assignment.doctorid == Doctor.id).exists())).\
    group_by(Doctor.id).\
    having(db.func.count() < n).\
    all()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM