选择具有匹配标记的所有项目

时间:2014-11-09 12:50:42

标签: python sql django postgresql sql-order-by

假设我有以下Django模型:

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)

添加一些数据:

            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

假设我们的用户喜欢tag1tag2tag6BBC。所有文章都符合要求,因为第1条有tag1tag2,第4条有tag1,第2条和第3条来自BBC

如果我们按评分排序:article4article3article1article2

但是,我需要按照他们拥有的匹配标签数量+博客优先订购商品,然后按等级作为第二个订购参数。所以我希望结果按以下顺序排列:

  1. 第1条 - tag1tag2,评分= 3
  2. 第2条 - tag1BBC,评分= 2
  3. 第4条 - tag6,评分= 9
  4. 第3条 - BBC,评分= 5
  5. 是否可以在Django中执行此操作?如果没有,那么PostgreSQL呢?

2 个答案:

答案 0 :(得分:1)

SQL查询可能如下所示:

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;

基本上:

  1. 在子查询ArticleID中按t计算匹配的代码。
  2. LEFT JOIN主要表格,包含辅助(blog)和第三(rating)排序条件的数据。
  3. ORDER BY三个条件,ct首先,blog下一个,rating最后一个。所有这些都下降(最高值第一)。这也适用于布尔表达式(a.blog = 'BBC'),因为TRUE(1)按降序排列FALSE(0)之前。
  4. 重要事项:按降序排列NULL值会先排序,因此如果可以为NULL值,则需要NULLS LAST(如果不能,则不会受到伤害) )。

    即使您的所有列都已定义NOT NULL,由于ctLEFT JOIN仍然可以为NULL。

    如果Django使用双引号保留混合大小写的名称,则必须在SQL中执行此操作。否则,所有标识符都将转换为小写。

答案 1 :(得分:1)

查询可能更简单我相信:) 这里没有真正需要加入。这是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