问题
Suppose I have the following Django models:
class Article(models.Model):
title = models.CharField(max_length=200)
blog = models.CharField(max_length=255)
rating = models.IntegerField(default=0)
class ArticleTag(models.Model):
article = models.ForeignKey(Article)
tag = models.CharField(max_length=200)
Add some data:
ArticleID Rating Blog
-----------------------------------------
article1 -> 1 3 CNN
article2 -> 2 2 BBC
article3 -> 3 5 BBC
article4 -> 4 9 NTV
ArticleID tag
-------------------
1 tag1
1 tag2
1 tag3
2 tag1
2 tag4
3 tag5
4 tag6
4 tag7
Suppose we have a user that likes tag1
, tag2
, tag6
and BBC
. All the articles match the requirements, because article1 has tag1
and tag2
, article4 has tag1
, article2 and article3 are from BBC
.
If we order them by rating: article4
, article3
, article1
, article2
.
However, I need to order items by the number of matching tags they have + blog first, and then by rating as the second ordering parameter. So I expect the results in the following order:
- article1 -
tag1
andtag2
, rating=3 - article2 -
tag1
andBBC
, rating=2 - article4 -
tag6
, rating=9 - article3 -
BBC
, rating=5
Is it possible to do this in Django? If not, what about PostgreSQL?
回答1:
The SQL query could look like this:
SELECT *
FROM Article a
LEFT JOIN (
SELECT ArticleID, count(*) AS ct
FROM ArticleTag
WHERE tag IN ('tag1', 'tag2', 'tag6') -- your tags here
GROUP BY ArticleID
) t ON t.ArticleID = a.ID
ORDER BY t.ct DESC NULLS LAST
, (a.blog = 'BBC') DESC NULLS LAST -- your blog here
, rating DESC NULLS LAST;
Basically:
- Count the matching tags per
ArticleID
in subqueryt
. LEFT JOIN
the main table to it with data for secondary (blog
) and tertiary (rating
) sort criteria.ORDER BY
the three criteria,ct
first,blog
next,rating
last. All of them descending (highest value first). That works for the boolean expression(a.blog = 'BBC')
as well, becauseTRUE
(1) sorts beforeFALSE
(0) in descending order.
Important: In descending order NULL values would sort first, so NULLS LAST
is needed if there can be NULL values (and does not hurt if there cannot).
- PostgreSQL sort by datetime asc, null first?
Even if all your columns are defined NOT NULL
, ct
can still be NULL due to the LEFT JOIN
.
If Django preserves mixed case names with double-quotes, you have to do that in SQL, too. Otherwise all identifiers are cast to lower case.
回答2:
The query could be simpler I believe :) There is no real need for a join here. Here's the sqlfiddle: http://sqlfiddle.com/#!2/1e565/10
SELECT
article.ArticleID,
COUNT(DISTINCT tag.tag),
COUNT(DISTINCT article.Blog LIKE 'BBC'),
COUNT(DISTINCT tag.tag) + COUNT(DISTINCT article.Blog LIKE 'BBC'),
article.rating
FROM article
LEFT JOIN tag
ON tag.ArticleID = article.ArticleID
WHERE tag.tag IN ('tag1', 'tag2', 'tag6') OR article.Blog LIKE 'BBC'
GROUP BY
article.ArticleID,
article.rating
ORDER BY
COUNT(DISTINCT tag.tag) + COUNT(DISTINCT article.Blog LIKE 'BBC') DESC,
rating DESC
来源:https://stackoverflow.com/questions/26828369/select-all-items-that-have-matching-tags