PostgreSQL - 按数组排序

cat*_*can 6 sql postgresql aggregate sql-order-by set-returning-functions

我有2个表 - 课程包含课程的id和名称以及包含每门课程标签的tagCourse.

course                    tagcourse
------------            ----------------
PK id_course            PK tag
   name                 PK, FK id_course
Run Code Online (Sandbox Code Playgroud)

我想编写一个函数,通过给定的标签数组搜索课程,并按匹配标签的数量排序.但是我不知道如何以有效的方式正确地编写它.请帮我.

即.

CREATE OR REPLACE FUNCTION searchByTags(tags varchar[])
RETURNS SETOF.....
  RETURN QUERY SELECT * FROM course c INNER JOIN tagcourse tc ON c.id_course = tc.id_course
  WHERE ???  ORDER BY ???

END....
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 4

CREATE OR REPLACE FUNCTION search_by_tags(tags varchar[])
  RETURNS TABLE (id_course integer, name text, tag_ct integer)
  LANGUAGE sql AS
$func$
   SELECT id_course, c.name, ct.tag_ct
   FROM  (
      SELECT tc.id_course, count(*)::int AS tag_ct
      FROM   unnest($1) x(tag)
      JOIN   tagcourse tc USING (tag)
      GROUP  BY 1                      -- first aggregate ..
      ) AS ct
   JOIN   course c USING (id_course)   -- .. then join
   ORDER  BY ct.tag_ct DESC            --  more columns to break ties?
$func$;
Run Code Online (Sandbox Code Playgroud)

用于unnest()从输入数组生成一个表,就像@Clodoaldo 已经演示的那样

为此,您不需要 plpgsql。使用普通 SQL 函数更简单。

我使用unnest($1)(withpositionalparameter) 而不是unnest(tags),因为后者仅对 SQL 函数中的 PostgreSQL 9.2+ 有效(与 plpgsql 不同)。手册:

在旧的数字方法中,使用以下语法引用参数$n$1引用第一个输入参数,$2引用第二个输入参数,依此类推。无论特定参数是否使用名称声明,这都将起作用。

count()返回bigint。您需要将其转换为int匹配声明的返回类型或声明返回的列作为bigint开始。

使用 (equi-joins) 来简化语法的完美时机USINGUSING (tag)而不是ON tc.tag = c.tag.

首先聚合然后连接到另一个表通常会更快。减少所需的连接操作。
为了解决@Clodoaldo的评论,这里有一个小提琴演示了差异:

db<>在这里
摆弄旧的sqlfiddle

OTOH,如果您在连接后聚合,则不需要子查询。更短,但可能更慢:

SELECT c.id_course, c.name, count(*)::int AS tag_ct
FROM   unnest($1) x(tag)
JOIN   tagcourse tc USING (tag)
JOIN   course     c USING (id_course)
GROUP  BY 1
ORDER  BY 3 DESC;  --  more columns to break ties?
Run Code Online (Sandbox Code Playgroud)