Postgresql:根据表列中的最大匹配数对结果进行排序

时间:2016-08-18 14:59:00

标签: sql ruby-on-rails postgresql

根据评论进行编辑

我有2个表帖子类别。结构和样本数据如下:

POSTS
ID    NAME                  DESCRIPTION              CATEGORY_ID
--  -----------------   -------------------          -----------
1   For Song Lovers     A post all about music            8
2   For Music Lovers    About passion of music            8
3   I love songs        Listing favourite songs           8
4   Rock Music          ImagineDragon                     6
5   Retro Music         Old choice musical themes         7

CATEGORIES
ID    NAME
--  -----------------
6   Artists
7   Entertainment
8   Music

我想以这样的方式对记录进行排序:

  1. 首先显示名称,描述和类别名称中匹配关键字的帖子,然后是
  2. 在名称中匹配关键字的帖子,描述后跟
  3. 名称中匹配关键字后跟
  4. 的帖子
  5. 描述中匹配关键字的帖子,后跟
  6. 类别
  7. 中匹配关键字的帖子

    我希望得到的结果是(如果 keyword = music ):

    OUTPUT
    ---------
    Post ID#2
    Post ID#5
    Post ID#4
    Post ID#1
    Post ID#3
    

    我已经能够编写5个查询并将其组合起来以获得唯一记录。但这不是优化的解决方案。这是我试过的:

    (
    SELECT "posts".* FROM "posts" INNER JOIN "categories" ON "categories"."id" = "posts"."category_id" WHERE posts.name iLIKE ('%music%') AND posts.description iLIKE ('%music%') AND categories.name iLIKE ('%music%')
    +
    SELECT "posts".* FROM "posts" WHERE posts.name iLIKE ('%music%') AND posts.description iLIKE ('%music%')
    +
    SELECT "posts".* FROM "posts"  WHERE posts.name iLIKE ('%music%')
    +
    SELECT "posts".* FROM "posts"  WHERE posts.description iLIKE ('%music%')
    +
    SELECT "posts".* FROM "posts" INNER JOIN "categories" ON "categories"."id" = "posts"."category_id" WHERE categories.name iLIKE ('%music%')
    ).uniq
    

    我甚至尝试编写单个查询,但这并没有返回我想要的结果(明显的原因是我没有使用GROUP OR COUNT)

    SELECT posts.* FROM posts INNER JOIN categories ON categories.id = posts.category_id
    WHERE (
      (posts.name iLIKE ('%music%') AND posts.description iLIKE ('%music%') AND categories.name iLIKE ('%music%'))
      OR (posts.name iLIKE ('%music%') AND posts.description iLIKE ('%music%'))
      OR (posts.name iLIKE ('%music%'))
      OR (posts.description iLIKE ('%music%'))
      OR (categories.name iLIKE ('%music%')))
    

    请建议我如何使用上述SQL实现相同的目标。

    环境详情:

    数据库:PostgreSQL

    版本:postgres(PostgreSQL)9.4.5

3 个答案:

答案 0 :(得分:3)

据我所知,PostgreSQL支持布尔值,因此这应符合您的描述,按单独的列排序:

SELECT posts.* FROM posts INNER JOIN categories ON categories.id = posts.category_id
WHERE  -- can be simplified to 
      LOWER(posts.name)        iLIKE ('%music%')
   OR LOWER(posts.description) iLIKE ('%music%')
   OR LOWER(categories.name)   iLIKE ('%music%')
ORDER BY -- FALSE probably sorts lower than TRUE, thus DESC
  LOWER(posts.name)        iLIKE ('%music%') DESC, posts.name,
  LOWER(posts.description) iLIKE ('%music%') DESC, posts.description
  LOWER(categories.name)   iLIKE ('%music%') DESC, categories.name

另一个评论:iLike不区分大小写,为什么然后应用LOWER?这应该返回相同的结果:

SELECT posts.* FROM posts INNER JOIN categories ON categories.id = posts.category_id
WHERE  -- can be simplified to 
      posts.name        iLIKE ('%music%')
   OR posts.description iLIKE ('%music%')
   OR categories.name   iLIKE ('%music%')
ORDER BY -- FALSE probably sorts lower than TRUE, thus DESC
   posts.name        iLIKE ('%music%') DESC, posts.name,
   posts.description iLIKE ('%music%') DESC, posts.description
   categories.name   iLIKE ('%music%') DESC, categories.name

现在结合@ JuanCarlosOropeza的答案: - )

答案 1 :(得分:2)

 ORDER BY 
       CASE WHEN LOWER(posts.name) iLIKE ('%music%') THEN posts.name END,
       CASE WHEN LOWER(posts.description) iLIKE ('%music%') THEN posts.description END,
       CASE WHEN LOWER(categories.name) iLIKE ('%music%') THEN categories.name END,

答案 2 :(得分:0)

感谢@ juan-carlos-oropeza和@dnoeth的回复。根据您的建议和摘要,我在下面提出并能够解决此问题

ORDER BY
  CASE WHEN posts.name iLIKE ('%music%') AND posts.description iLIKE ('%music%') AND categories.name iLIKE ('%music%') THEN 0 END,
  CASE WHEN posts.name iLIKE ('%music%') AND posts.description iLIKE ('%music%') THEN 1 END,
  CASE WHEN posts.name iLIKE ('%music%') THEN 2 END,
  CASE WHEN posts.description iLIKE ('%music%') THEN 3 END,
  CASE WHEN categories.name iLIKE ('%music%') THEN 4 END