我有2个表 - 课程包含课程的ID和名称以及包含每门课程标签的tagCourse。
course tagcourse
------------ ----------------
PK id_course PK tag
name PK, FK id_course
我想编写一个按给定的标签数组搜索课程的函数,并按照匹配标签的数量对它们进行排序。但是,我不知道如何以有效的方式正确地编写它。请帮帮我。
即
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....
答案 0 :(得分:4)
CREATE OR REPLACE FUNCTION search_by_tags(tags varchar[])
RETURNS TABLE (id_course integer, name text, tag_ct integer) 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$ LANGUAGE sql;
使用unnest()
从输入数组生成表格,例如already demonstrated by @Clodoaldo。
你不需要plpgsql。使用普通的SQL函数更简单。
我使用unnest($1)
(带位置参数)而不是unnest(tags)
,因为后者仅对SQL函数中的PostgreSQL 9.2+有效(与plpgsql不同)。 I quote the manual here:
在旧的数值方法中,使用。引用参数 语法
$n
:$1
引用第一个输入参数$2
到第二个, 等等。无论具体论证是什么,这都将起作用 用名字声明。
count()
返回bigint
。您需要将其强制转换为int
以匹配声明的返回类型,或者将返回的列声明为bigint
以开始。
使用USING
(等连接)简化语法的完美场合:USING (tag)
而不是ON tc.tag = c.tag
。
首次聚合通常会更快,然后加入另一个表。减少所需的连接操作 根据{{3}}的问题,这里有@Clodoaldo in the comments来证明其差异。
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?
答案 1 :(得分:0)
create or replace function searchByTags(tags varchar[])
returns table (id_course integer, name text, quantitiy integer)
as $$
select *
from (
select c.id_course, c.name, count(*) quantity
from
course c
inner join
tagcourse tc on c.id_course = tc.id_course
inner join
unnest(tags) s(tag) on s.tag = tc.tag
group by c.id_course, c.name
) s
order by quantity desc, name
;
$$ language sql;