Flask-SQLAlchemy query for count












2














I'am using Flask-SQLAlchemy and i use one-to-many relationships. Two models



class Request(db.Model):

id = db.Column(db.Integer, primary_key = True)
r_time = db.Column(db.DateTime, index = True, default=datetime.utcnow)
org = db.Column(db.String(120))
dest = db.Column(db.String(120))
buyer_id = db.Column(db.Integer, db.ForeignKey('buyer.id'))
sale_id = db.Column(db.Integer, db.ForeignKey('sale.id'))
cost = db.Column(db.Integer)
sr = db.Column(db.Integer)
profit = db.Column(db.Integer)

def __repr__(self):
return '<Request {} by {}>'.format(self.org, self.buyer_id)

class Buyer(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(120), unique = True)
email = db.Column(db.String(120), unique = True)
requests = db.relationship('Request', backref = 'buyer', lazy='dynamic')

def __repr__(self):
return '<Buyer {}>'.format(self.name)


I need to identify which Buyer has a minimum requests from all
of the buyers.



I could do it manually by creating additional lists and put all
requests in a lists and search for the list. But I believe there is another simple way to do it via SQLAlchemy query










share|improve this question




















  • 1




    You mean you need to find the one buyer with the fewest requests? That's different from a query for count.
    – Martijn Pieters
    Nov 21 at 8:10






  • 1




    What should happen if there is a tie? Say two buyers have each just one request, and all other buyers have more. Should both buyers be returned? Or just one? If so, which one?
    – Martijn Pieters
    Nov 21 at 8:11










  • Possible duplicate of SQLAlchemy ordering by count on a many to many relationship
    – vishes_shell
    Nov 21 at 8:11










  • @vishes_shell: many-to-many and many-to-one differ quite a bit, and this question is about finding a single or a small subset of results.
    – Martijn Pieters
    Nov 21 at 8:14










  • next question for the OP: should buyers without any requests (Buyer.requests.count() == 0) be ignored?
    – Martijn Pieters
    Nov 21 at 8:14
















2














I'am using Flask-SQLAlchemy and i use one-to-many relationships. Two models



class Request(db.Model):

id = db.Column(db.Integer, primary_key = True)
r_time = db.Column(db.DateTime, index = True, default=datetime.utcnow)
org = db.Column(db.String(120))
dest = db.Column(db.String(120))
buyer_id = db.Column(db.Integer, db.ForeignKey('buyer.id'))
sale_id = db.Column(db.Integer, db.ForeignKey('sale.id'))
cost = db.Column(db.Integer)
sr = db.Column(db.Integer)
profit = db.Column(db.Integer)

def __repr__(self):
return '<Request {} by {}>'.format(self.org, self.buyer_id)

class Buyer(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(120), unique = True)
email = db.Column(db.String(120), unique = True)
requests = db.relationship('Request', backref = 'buyer', lazy='dynamic')

def __repr__(self):
return '<Buyer {}>'.format(self.name)


I need to identify which Buyer has a minimum requests from all
of the buyers.



I could do it manually by creating additional lists and put all
requests in a lists and search for the list. But I believe there is another simple way to do it via SQLAlchemy query










share|improve this question




















  • 1




    You mean you need to find the one buyer with the fewest requests? That's different from a query for count.
    – Martijn Pieters
    Nov 21 at 8:10






  • 1




    What should happen if there is a tie? Say two buyers have each just one request, and all other buyers have more. Should both buyers be returned? Or just one? If so, which one?
    – Martijn Pieters
    Nov 21 at 8:11










  • Possible duplicate of SQLAlchemy ordering by count on a many to many relationship
    – vishes_shell
    Nov 21 at 8:11










  • @vishes_shell: many-to-many and many-to-one differ quite a bit, and this question is about finding a single or a small subset of results.
    – Martijn Pieters
    Nov 21 at 8:14










  • next question for the OP: should buyers without any requests (Buyer.requests.count() == 0) be ignored?
    – Martijn Pieters
    Nov 21 at 8:14














2












2








2







I'am using Flask-SQLAlchemy and i use one-to-many relationships. Two models



class Request(db.Model):

id = db.Column(db.Integer, primary_key = True)
r_time = db.Column(db.DateTime, index = True, default=datetime.utcnow)
org = db.Column(db.String(120))
dest = db.Column(db.String(120))
buyer_id = db.Column(db.Integer, db.ForeignKey('buyer.id'))
sale_id = db.Column(db.Integer, db.ForeignKey('sale.id'))
cost = db.Column(db.Integer)
sr = db.Column(db.Integer)
profit = db.Column(db.Integer)

def __repr__(self):
return '<Request {} by {}>'.format(self.org, self.buyer_id)

class Buyer(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(120), unique = True)
email = db.Column(db.String(120), unique = True)
requests = db.relationship('Request', backref = 'buyer', lazy='dynamic')

def __repr__(self):
return '<Buyer {}>'.format(self.name)


I need to identify which Buyer has a minimum requests from all
of the buyers.



I could do it manually by creating additional lists and put all
requests in a lists and search for the list. But I believe there is another simple way to do it via SQLAlchemy query










share|improve this question















I'am using Flask-SQLAlchemy and i use one-to-many relationships. Two models



class Request(db.Model):

id = db.Column(db.Integer, primary_key = True)
r_time = db.Column(db.DateTime, index = True, default=datetime.utcnow)
org = db.Column(db.String(120))
dest = db.Column(db.String(120))
buyer_id = db.Column(db.Integer, db.ForeignKey('buyer.id'))
sale_id = db.Column(db.Integer, db.ForeignKey('sale.id'))
cost = db.Column(db.Integer)
sr = db.Column(db.Integer)
profit = db.Column(db.Integer)

def __repr__(self):
return '<Request {} by {}>'.format(self.org, self.buyer_id)

class Buyer(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(120), unique = True)
email = db.Column(db.String(120), unique = True)
requests = db.relationship('Request', backref = 'buyer', lazy='dynamic')

def __repr__(self):
return '<Buyer {}>'.format(self.name)


I need to identify which Buyer has a minimum requests from all
of the buyers.



I could do it manually by creating additional lists and put all
requests in a lists and search for the list. But I believe there is another simple way to do it via SQLAlchemy query







python sql sqlalchemy flask-sqlalchemy






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 21 at 20:20









Martijn Pieters

699k13124202259




699k13124202259










asked Nov 21 at 8:07









J_log

285




285








  • 1




    You mean you need to find the one buyer with the fewest requests? That's different from a query for count.
    – Martijn Pieters
    Nov 21 at 8:10






  • 1




    What should happen if there is a tie? Say two buyers have each just one request, and all other buyers have more. Should both buyers be returned? Or just one? If so, which one?
    – Martijn Pieters
    Nov 21 at 8:11










  • Possible duplicate of SQLAlchemy ordering by count on a many to many relationship
    – vishes_shell
    Nov 21 at 8:11










  • @vishes_shell: many-to-many and many-to-one differ quite a bit, and this question is about finding a single or a small subset of results.
    – Martijn Pieters
    Nov 21 at 8:14










  • next question for the OP: should buyers without any requests (Buyer.requests.count() == 0) be ignored?
    – Martijn Pieters
    Nov 21 at 8:14














  • 1




    You mean you need to find the one buyer with the fewest requests? That's different from a query for count.
    – Martijn Pieters
    Nov 21 at 8:10






  • 1




    What should happen if there is a tie? Say two buyers have each just one request, and all other buyers have more. Should both buyers be returned? Or just one? If so, which one?
    – Martijn Pieters
    Nov 21 at 8:11










  • Possible duplicate of SQLAlchemy ordering by count on a many to many relationship
    – vishes_shell
    Nov 21 at 8:11










  • @vishes_shell: many-to-many and many-to-one differ quite a bit, and this question is about finding a single or a small subset of results.
    – Martijn Pieters
    Nov 21 at 8:14










  • next question for the OP: should buyers without any requests (Buyer.requests.count() == 0) be ignored?
    – Martijn Pieters
    Nov 21 at 8:14








1




1




You mean you need to find the one buyer with the fewest requests? That's different from a query for count.
– Martijn Pieters
Nov 21 at 8:10




You mean you need to find the one buyer with the fewest requests? That's different from a query for count.
– Martijn Pieters
Nov 21 at 8:10




1




1




What should happen if there is a tie? Say two buyers have each just one request, and all other buyers have more. Should both buyers be returned? Or just one? If so, which one?
– Martijn Pieters
Nov 21 at 8:11




What should happen if there is a tie? Say two buyers have each just one request, and all other buyers have more. Should both buyers be returned? Or just one? If so, which one?
– Martijn Pieters
Nov 21 at 8:11












Possible duplicate of SQLAlchemy ordering by count on a many to many relationship
– vishes_shell
Nov 21 at 8:11




Possible duplicate of SQLAlchemy ordering by count on a many to many relationship
– vishes_shell
Nov 21 at 8:11












@vishes_shell: many-to-many and many-to-one differ quite a bit, and this question is about finding a single or a small subset of results.
– Martijn Pieters
Nov 21 at 8:14




@vishes_shell: many-to-many and many-to-one differ quite a bit, and this question is about finding a single or a small subset of results.
– Martijn Pieters
Nov 21 at 8:14












next question for the OP: should buyers without any requests (Buyer.requests.count() == 0) be ignored?
– Martijn Pieters
Nov 21 at 8:14




next question for the OP: should buyers without any requests (Buyer.requests.count() == 0) be ignored?
– Martijn Pieters
Nov 21 at 8:14












1 Answer
1






active

oldest

votes


















1














You can do this with a CTE (common table expression) for a select that produces buyer ids together with their request counts, so



buyer_id | request_count
:------- | :------------
1 | 5
2 | 3
3 | 1
4 | 1


You can filter here on the counts having to be greater than 0 to be listed.



You can then join the buyers table against that to produce:



buyer_id | buyer_name | buyer_email      | request_count
:------- | :--------- | :--------------- | :------------
1 | foo | foo@example.com | 5
2 | bar | bar@example.com | 3
3 | baz | baz@example.com | 1
4 | spam | spam@example.com | 1


but because we are using a CTE, you can also query the CTE for the lowest count value. In the above example, that's 1, and you can add a WHERE clause to the joined buyer-with-cte-counts query to filter the results down to only rows where the request_count value is equal to that minimum number.



The SQL query for this is



WITH request_counts AS (
SELECT request.buyer_id AS buyer_id, count(request.id) AS request_count
FROM request GROUP BY request.buyer_id
HAVING count(request.id) > ?
)
SELECT buyer.*
FROM buyer
JOIN request_counts ON buyer.id = request_counts.buyer_id
WHERE request_counts.request_count = (
SELECT min(request_counts.request_count)
FROM request_counts
)


The WITH request_counts AS (...) defines a CTE, and it is that part that would produce the first table with buyer_id and request_count. The request_count table is then joined with request and the WHERE clause does the filtering on the min(request_counts.request_count) value.



Translating the above to Flask-SQLAlchemy code:



request_count = db.func.count(Request.id).label("request_count")
cte = (
db.select([Request.buyer_id.label("buyer_id"), request_count])
.group_by(Request.buyer_id)
.having(request_count > 0)
.cte('request_counts')
)
min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
buyers_with_least_requests = Buyer.query.join(
cte, Buyer.id == cte.c.buyer_id
).filter(cte.c.request_count == min_request_count).all()


Demo:



>>> __ = db.session.bulk_insert_mappings(
... Buyer, [{"name": n} for n in ("foo", "bar", "baz", "spam", "no requests")]
... )
>>> buyers = Buyer.query.order_by(Buyer.id).all()
>>> requests = [
... Request(buyer_id=b.id)
... for b in [*([buyers[0]] * 3), *([buyers[1]] * 5), *[buyers[2], buyers[3]]]
... ]
>>> __ = db.session.add_all(requests)
>>> request_count = db.func.count(Request.id).label("request_count")
>>> cte = (
... db.select([Request.buyer_id.label("buyer_id"), request_count])
... .group_by(Request.buyer_id)
... .having(request_count > 0)
... .cte("request_counts")
... )
>>> buyers_w_counts = Buyer.query.join(cte, cte.c.buyer_id == Buyer.id)
>>> for buyer, count in buyers_w_counts.add_column(cte.c.request_count):
... # print out buyer and request count for this demo
... print(buyer, count, sep=": ")
<Buyer foo>: 3
<Buyer bar>: 5
<Buyer baz>: 1
<Buyer spam>: 1
>>> min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
>>> buyers_w_counts.filter(cte.c.request_count == min_request_count).all()
[<Buyer baz>, <Buyer spam>]


I've also created a db<>fiddle here, containing the same queries, to play with.






share|improve this answer























  • Thanks a lot for your help i will try to understand your code. It will takes a time for me but i will. May be you could advise some good tutorial about complicated queries and how it need to be done. For me it's complicated i'm new in SQLAlchemy.
    – J_log
    Nov 21 at 17:50








  • 1




    @J_log: What I did here was use SQL, translated to SQLAlchemy. Master SQL first if you want to learn how to construct queries like these, the sql tag wiki has a section on tutorials and books. But don't despair, it took me a while to learn tricks like CTEs and proper aggregation and such.
    – Martijn Pieters
    Nov 21 at 17:56










  • @J_log: I've reworked the answer to add more explanation as to what is going on.
    – Martijn Pieters
    Nov 21 at 20:04











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53407641%2fflask-sqlalchemy-query-for-count%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














You can do this with a CTE (common table expression) for a select that produces buyer ids together with their request counts, so



buyer_id | request_count
:------- | :------------
1 | 5
2 | 3
3 | 1
4 | 1


You can filter here on the counts having to be greater than 0 to be listed.



You can then join the buyers table against that to produce:



buyer_id | buyer_name | buyer_email      | request_count
:------- | :--------- | :--------------- | :------------
1 | foo | foo@example.com | 5
2 | bar | bar@example.com | 3
3 | baz | baz@example.com | 1
4 | spam | spam@example.com | 1


but because we are using a CTE, you can also query the CTE for the lowest count value. In the above example, that's 1, and you can add a WHERE clause to the joined buyer-with-cte-counts query to filter the results down to only rows where the request_count value is equal to that minimum number.



The SQL query for this is



WITH request_counts AS (
SELECT request.buyer_id AS buyer_id, count(request.id) AS request_count
FROM request GROUP BY request.buyer_id
HAVING count(request.id) > ?
)
SELECT buyer.*
FROM buyer
JOIN request_counts ON buyer.id = request_counts.buyer_id
WHERE request_counts.request_count = (
SELECT min(request_counts.request_count)
FROM request_counts
)


The WITH request_counts AS (...) defines a CTE, and it is that part that would produce the first table with buyer_id and request_count. The request_count table is then joined with request and the WHERE clause does the filtering on the min(request_counts.request_count) value.



Translating the above to Flask-SQLAlchemy code:



request_count = db.func.count(Request.id).label("request_count")
cte = (
db.select([Request.buyer_id.label("buyer_id"), request_count])
.group_by(Request.buyer_id)
.having(request_count > 0)
.cte('request_counts')
)
min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
buyers_with_least_requests = Buyer.query.join(
cte, Buyer.id == cte.c.buyer_id
).filter(cte.c.request_count == min_request_count).all()


Demo:



>>> __ = db.session.bulk_insert_mappings(
... Buyer, [{"name": n} for n in ("foo", "bar", "baz", "spam", "no requests")]
... )
>>> buyers = Buyer.query.order_by(Buyer.id).all()
>>> requests = [
... Request(buyer_id=b.id)
... for b in [*([buyers[0]] * 3), *([buyers[1]] * 5), *[buyers[2], buyers[3]]]
... ]
>>> __ = db.session.add_all(requests)
>>> request_count = db.func.count(Request.id).label("request_count")
>>> cte = (
... db.select([Request.buyer_id.label("buyer_id"), request_count])
... .group_by(Request.buyer_id)
... .having(request_count > 0)
... .cte("request_counts")
... )
>>> buyers_w_counts = Buyer.query.join(cte, cte.c.buyer_id == Buyer.id)
>>> for buyer, count in buyers_w_counts.add_column(cte.c.request_count):
... # print out buyer and request count for this demo
... print(buyer, count, sep=": ")
<Buyer foo>: 3
<Buyer bar>: 5
<Buyer baz>: 1
<Buyer spam>: 1
>>> min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
>>> buyers_w_counts.filter(cte.c.request_count == min_request_count).all()
[<Buyer baz>, <Buyer spam>]


I've also created a db<>fiddle here, containing the same queries, to play with.






share|improve this answer























  • Thanks a lot for your help i will try to understand your code. It will takes a time for me but i will. May be you could advise some good tutorial about complicated queries and how it need to be done. For me it's complicated i'm new in SQLAlchemy.
    – J_log
    Nov 21 at 17:50








  • 1




    @J_log: What I did here was use SQL, translated to SQLAlchemy. Master SQL first if you want to learn how to construct queries like these, the sql tag wiki has a section on tutorials and books. But don't despair, it took me a while to learn tricks like CTEs and proper aggregation and such.
    – Martijn Pieters
    Nov 21 at 17:56










  • @J_log: I've reworked the answer to add more explanation as to what is going on.
    – Martijn Pieters
    Nov 21 at 20:04
















1














You can do this with a CTE (common table expression) for a select that produces buyer ids together with their request counts, so



buyer_id | request_count
:------- | :------------
1 | 5
2 | 3
3 | 1
4 | 1


You can filter here on the counts having to be greater than 0 to be listed.



You can then join the buyers table against that to produce:



buyer_id | buyer_name | buyer_email      | request_count
:------- | :--------- | :--------------- | :------------
1 | foo | foo@example.com | 5
2 | bar | bar@example.com | 3
3 | baz | baz@example.com | 1
4 | spam | spam@example.com | 1


but because we are using a CTE, you can also query the CTE for the lowest count value. In the above example, that's 1, and you can add a WHERE clause to the joined buyer-with-cte-counts query to filter the results down to only rows where the request_count value is equal to that minimum number.



The SQL query for this is



WITH request_counts AS (
SELECT request.buyer_id AS buyer_id, count(request.id) AS request_count
FROM request GROUP BY request.buyer_id
HAVING count(request.id) > ?
)
SELECT buyer.*
FROM buyer
JOIN request_counts ON buyer.id = request_counts.buyer_id
WHERE request_counts.request_count = (
SELECT min(request_counts.request_count)
FROM request_counts
)


The WITH request_counts AS (...) defines a CTE, and it is that part that would produce the first table with buyer_id and request_count. The request_count table is then joined with request and the WHERE clause does the filtering on the min(request_counts.request_count) value.



Translating the above to Flask-SQLAlchemy code:



request_count = db.func.count(Request.id).label("request_count")
cte = (
db.select([Request.buyer_id.label("buyer_id"), request_count])
.group_by(Request.buyer_id)
.having(request_count > 0)
.cte('request_counts')
)
min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
buyers_with_least_requests = Buyer.query.join(
cte, Buyer.id == cte.c.buyer_id
).filter(cte.c.request_count == min_request_count).all()


Demo:



>>> __ = db.session.bulk_insert_mappings(
... Buyer, [{"name": n} for n in ("foo", "bar", "baz", "spam", "no requests")]
... )
>>> buyers = Buyer.query.order_by(Buyer.id).all()
>>> requests = [
... Request(buyer_id=b.id)
... for b in [*([buyers[0]] * 3), *([buyers[1]] * 5), *[buyers[2], buyers[3]]]
... ]
>>> __ = db.session.add_all(requests)
>>> request_count = db.func.count(Request.id).label("request_count")
>>> cte = (
... db.select([Request.buyer_id.label("buyer_id"), request_count])
... .group_by(Request.buyer_id)
... .having(request_count > 0)
... .cte("request_counts")
... )
>>> buyers_w_counts = Buyer.query.join(cte, cte.c.buyer_id == Buyer.id)
>>> for buyer, count in buyers_w_counts.add_column(cte.c.request_count):
... # print out buyer and request count for this demo
... print(buyer, count, sep=": ")
<Buyer foo>: 3
<Buyer bar>: 5
<Buyer baz>: 1
<Buyer spam>: 1
>>> min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
>>> buyers_w_counts.filter(cte.c.request_count == min_request_count).all()
[<Buyer baz>, <Buyer spam>]


I've also created a db<>fiddle here, containing the same queries, to play with.






share|improve this answer























  • Thanks a lot for your help i will try to understand your code. It will takes a time for me but i will. May be you could advise some good tutorial about complicated queries and how it need to be done. For me it's complicated i'm new in SQLAlchemy.
    – J_log
    Nov 21 at 17:50








  • 1




    @J_log: What I did here was use SQL, translated to SQLAlchemy. Master SQL first if you want to learn how to construct queries like these, the sql tag wiki has a section on tutorials and books. But don't despair, it took me a while to learn tricks like CTEs and proper aggregation and such.
    – Martijn Pieters
    Nov 21 at 17:56










  • @J_log: I've reworked the answer to add more explanation as to what is going on.
    – Martijn Pieters
    Nov 21 at 20:04














1












1








1






You can do this with a CTE (common table expression) for a select that produces buyer ids together with their request counts, so



buyer_id | request_count
:------- | :------------
1 | 5
2 | 3
3 | 1
4 | 1


You can filter here on the counts having to be greater than 0 to be listed.



You can then join the buyers table against that to produce:



buyer_id | buyer_name | buyer_email      | request_count
:------- | :--------- | :--------------- | :------------
1 | foo | foo@example.com | 5
2 | bar | bar@example.com | 3
3 | baz | baz@example.com | 1
4 | spam | spam@example.com | 1


but because we are using a CTE, you can also query the CTE for the lowest count value. In the above example, that's 1, and you can add a WHERE clause to the joined buyer-with-cte-counts query to filter the results down to only rows where the request_count value is equal to that minimum number.



The SQL query for this is



WITH request_counts AS (
SELECT request.buyer_id AS buyer_id, count(request.id) AS request_count
FROM request GROUP BY request.buyer_id
HAVING count(request.id) > ?
)
SELECT buyer.*
FROM buyer
JOIN request_counts ON buyer.id = request_counts.buyer_id
WHERE request_counts.request_count = (
SELECT min(request_counts.request_count)
FROM request_counts
)


The WITH request_counts AS (...) defines a CTE, and it is that part that would produce the first table with buyer_id and request_count. The request_count table is then joined with request and the WHERE clause does the filtering on the min(request_counts.request_count) value.



Translating the above to Flask-SQLAlchemy code:



request_count = db.func.count(Request.id).label("request_count")
cte = (
db.select([Request.buyer_id.label("buyer_id"), request_count])
.group_by(Request.buyer_id)
.having(request_count > 0)
.cte('request_counts')
)
min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
buyers_with_least_requests = Buyer.query.join(
cte, Buyer.id == cte.c.buyer_id
).filter(cte.c.request_count == min_request_count).all()


Demo:



>>> __ = db.session.bulk_insert_mappings(
... Buyer, [{"name": n} for n in ("foo", "bar", "baz", "spam", "no requests")]
... )
>>> buyers = Buyer.query.order_by(Buyer.id).all()
>>> requests = [
... Request(buyer_id=b.id)
... for b in [*([buyers[0]] * 3), *([buyers[1]] * 5), *[buyers[2], buyers[3]]]
... ]
>>> __ = db.session.add_all(requests)
>>> request_count = db.func.count(Request.id).label("request_count")
>>> cte = (
... db.select([Request.buyer_id.label("buyer_id"), request_count])
... .group_by(Request.buyer_id)
... .having(request_count > 0)
... .cte("request_counts")
... )
>>> buyers_w_counts = Buyer.query.join(cte, cte.c.buyer_id == Buyer.id)
>>> for buyer, count in buyers_w_counts.add_column(cte.c.request_count):
... # print out buyer and request count for this demo
... print(buyer, count, sep=": ")
<Buyer foo>: 3
<Buyer bar>: 5
<Buyer baz>: 1
<Buyer spam>: 1
>>> min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
>>> buyers_w_counts.filter(cte.c.request_count == min_request_count).all()
[<Buyer baz>, <Buyer spam>]


I've also created a db<>fiddle here, containing the same queries, to play with.






share|improve this answer














You can do this with a CTE (common table expression) for a select that produces buyer ids together with their request counts, so



buyer_id | request_count
:------- | :------------
1 | 5
2 | 3
3 | 1
4 | 1


You can filter here on the counts having to be greater than 0 to be listed.



You can then join the buyers table against that to produce:



buyer_id | buyer_name | buyer_email      | request_count
:------- | :--------- | :--------------- | :------------
1 | foo | foo@example.com | 5
2 | bar | bar@example.com | 3
3 | baz | baz@example.com | 1
4 | spam | spam@example.com | 1


but because we are using a CTE, you can also query the CTE for the lowest count value. In the above example, that's 1, and you can add a WHERE clause to the joined buyer-with-cte-counts query to filter the results down to only rows where the request_count value is equal to that minimum number.



The SQL query for this is



WITH request_counts AS (
SELECT request.buyer_id AS buyer_id, count(request.id) AS request_count
FROM request GROUP BY request.buyer_id
HAVING count(request.id) > ?
)
SELECT buyer.*
FROM buyer
JOIN request_counts ON buyer.id = request_counts.buyer_id
WHERE request_counts.request_count = (
SELECT min(request_counts.request_count)
FROM request_counts
)


The WITH request_counts AS (...) defines a CTE, and it is that part that would produce the first table with buyer_id and request_count. The request_count table is then joined with request and the WHERE clause does the filtering on the min(request_counts.request_count) value.



Translating the above to Flask-SQLAlchemy code:



request_count = db.func.count(Request.id).label("request_count")
cte = (
db.select([Request.buyer_id.label("buyer_id"), request_count])
.group_by(Request.buyer_id)
.having(request_count > 0)
.cte('request_counts')
)
min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
buyers_with_least_requests = Buyer.query.join(
cte, Buyer.id == cte.c.buyer_id
).filter(cte.c.request_count == min_request_count).all()


Demo:



>>> __ = db.session.bulk_insert_mappings(
... Buyer, [{"name": n} for n in ("foo", "bar", "baz", "spam", "no requests")]
... )
>>> buyers = Buyer.query.order_by(Buyer.id).all()
>>> requests = [
... Request(buyer_id=b.id)
... for b in [*([buyers[0]] * 3), *([buyers[1]] * 5), *[buyers[2], buyers[3]]]
... ]
>>> __ = db.session.add_all(requests)
>>> request_count = db.func.count(Request.id).label("request_count")
>>> cte = (
... db.select([Request.buyer_id.label("buyer_id"), request_count])
... .group_by(Request.buyer_id)
... .having(request_count > 0)
... .cte("request_counts")
... )
>>> buyers_w_counts = Buyer.query.join(cte, cte.c.buyer_id == Buyer.id)
>>> for buyer, count in buyers_w_counts.add_column(cte.c.request_count):
... # print out buyer and request count for this demo
... print(buyer, count, sep=": ")
<Buyer foo>: 3
<Buyer bar>: 5
<Buyer baz>: 1
<Buyer spam>: 1
>>> min_request_count = db.select([db.func.min(cte.c.request_count)]).as_scalar()
>>> buyers_w_counts.filter(cte.c.request_count == min_request_count).all()
[<Buyer baz>, <Buyer spam>]


I've also created a db<>fiddle here, containing the same queries, to play with.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 21 at 20:20

























answered Nov 21 at 14:59









Martijn Pieters

699k13124202259




699k13124202259












  • Thanks a lot for your help i will try to understand your code. It will takes a time for me but i will. May be you could advise some good tutorial about complicated queries and how it need to be done. For me it's complicated i'm new in SQLAlchemy.
    – J_log
    Nov 21 at 17:50








  • 1




    @J_log: What I did here was use SQL, translated to SQLAlchemy. Master SQL first if you want to learn how to construct queries like these, the sql tag wiki has a section on tutorials and books. But don't despair, it took me a while to learn tricks like CTEs and proper aggregation and such.
    – Martijn Pieters
    Nov 21 at 17:56










  • @J_log: I've reworked the answer to add more explanation as to what is going on.
    – Martijn Pieters
    Nov 21 at 20:04


















  • Thanks a lot for your help i will try to understand your code. It will takes a time for me but i will. May be you could advise some good tutorial about complicated queries and how it need to be done. For me it's complicated i'm new in SQLAlchemy.
    – J_log
    Nov 21 at 17:50








  • 1




    @J_log: What I did here was use SQL, translated to SQLAlchemy. Master SQL first if you want to learn how to construct queries like these, the sql tag wiki has a section on tutorials and books. But don't despair, it took me a while to learn tricks like CTEs and proper aggregation and such.
    – Martijn Pieters
    Nov 21 at 17:56










  • @J_log: I've reworked the answer to add more explanation as to what is going on.
    – Martijn Pieters
    Nov 21 at 20:04
















Thanks a lot for your help i will try to understand your code. It will takes a time for me but i will. May be you could advise some good tutorial about complicated queries and how it need to be done. For me it's complicated i'm new in SQLAlchemy.
– J_log
Nov 21 at 17:50






Thanks a lot for your help i will try to understand your code. It will takes a time for me but i will. May be you could advise some good tutorial about complicated queries and how it need to be done. For me it's complicated i'm new in SQLAlchemy.
– J_log
Nov 21 at 17:50






1




1




@J_log: What I did here was use SQL, translated to SQLAlchemy. Master SQL first if you want to learn how to construct queries like these, the sql tag wiki has a section on tutorials and books. But don't despair, it took me a while to learn tricks like CTEs and proper aggregation and such.
– Martijn Pieters
Nov 21 at 17:56




@J_log: What I did here was use SQL, translated to SQLAlchemy. Master SQL first if you want to learn how to construct queries like these, the sql tag wiki has a section on tutorials and books. But don't despair, it took me a while to learn tricks like CTEs and proper aggregation and such.
– Martijn Pieters
Nov 21 at 17:56












@J_log: I've reworked the answer to add more explanation as to what is going on.
– Martijn Pieters
Nov 21 at 20:04




@J_log: I've reworked the answer to add more explanation as to what is going on.
– Martijn Pieters
Nov 21 at 20:04


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53407641%2fflask-sqlalchemy-query-for-count%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

404 Error Contact Form 7 ajax form submitting

How to know if a Active Directory user can login interactively

TypeError: fit_transform() missing 1 required positional argument: 'X'