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)
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) 来简化语法的完美时机USING:USING (tag)而不是ON tc.tag = c.tag.
首先聚合然后连接到另一个表通常会更快。减少所需的连接操作。
为了解决@Clodoaldo的评论,这里有一个小提琴演示了差异:
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)