我有一个函数根据名称返回一个人的id,这不是唯一的。参数是姓氏,名字和组织; firstname参数可以是名称,其首字母或NULL。以下是选择其中一个名称的关键查询(函数init()
返回字符串的第一个字母,后跟一个完整的句点):
SELECT p.id INTO _p FROM person p
WHERE p.lastname = _lastname AND ( _firstname IS NULL
OR inic(p.firstname) = _inic )
ORDER BY (p.firstname = _firstname) DESC, (p.organization = _org ) DESC,
( SELECT person_score( p.id ) ) DESC
LIMIT 1;
person_score
功能非常昂贵 - 它会在几个表中搜索此人的过去活动。数据库未满(测试表中只有几千行)并且重要列上有索引,但仍然调用评分函数使得人员返回功能慢七倍。如果它只减慢了真正需要排序的查询,那么这不会成为问题 - 为同一组织工作的完全同名很少见。不幸的是,EXPLAIN ANALYZE显示即使只有一个具有给定姓氏的人也会调用评分函数。
有没有办法确保只在需要时才评估最后一个条件而不会将查询分解为更多查询?如果没有,如何尽可能快地进行拆分(并在它们之间传递数据)?
一种可能的解决方案是将查询排在名字和组织的等效性之上,将其存储在数组中(而不是像现在这样的普通整数),然后仅在数组长度大于1时才运行评分函数。然而,这感觉很笨拙,我担心在普通情况下这不会使功能更快(对于同名人来说速度更慢)。我还有一个非常粗略的想法,如何做到这一点,我不想在我知道它是必要的之前开始尝试。
答案 0 :(得分:2)
因此,如果有多个结果记录并且您按(p.firstname = _firstname) DESC, (p.organization = _org ) DESC
订购并且仍然会获得重复项,那么您希望将该函数应用于排名第一的记录。因此,在ORDER BY中使用带有窗口函数的CASE构造。
SELECT p.id INTO _p
FROM person p
WHERE p.lastname = _lastname
AND ( _firstname IS NULL OR inic(p.firstname) = _inic)
ORDER BY (p.firstname = _firstname) DESC, (p.organization = _org) DESC,
CASE WHEN COUNT(*) OVER() > 1 THEN
CASE WHEN COUNT(*) OVER(PARTITION BY (p.firstname = _firstname), (p.organization = _org)) > 1 THEN
CASE WHEN RANK() OVER(ORDER BY (p.firstname = _firstname) DESC, (p.organization = _org) DESC) = 1 THEN
person_score(p.id)
END
END
END
LIMIT 1;
这可以节省一些不必要的函数调用。但是,dbms必须进行一些聚合才能确定是否必须进行调用。所以这可能更快或更快。试试吧。
答案 1 :(得分:2)
当您需要在查询中使用相当耗时的计算时,如果可能,最佳解决方案是在另一个表中预先计算该值。
通常情况下,当您拥有包含大量条目的帐户时,您需要经常查询余额。通常,这些系统会将帐户余额保存在不同的表中,并保持更新。
预先计算的值有两种可能性:
它们必须准确,并且可以实时提供:在这种情况下,您需要实施一个让它们保持最新的解决方案。理想情况下,应用程序应负责这样做,但在某些情况下,您无法修改应用程序,例如,因为它不是您的应用程序,或者数据来自多个不同的应用程序。在这些情况下,您可以使用触发器
他们不需要准确并且实时可用:您仍然可以使用以前的一些解决方案,或者您可以更简单的方式实现:准备存储过程或查询计算所需的数据并在需要时运行它,例如手动或作为一个recurent工作(理想情况是在低工作负载时间,如果它太耗时),或者由某种事件引起的。
在您的特定情况下,您需要一张表来保留用户的分数,并实施任何此解决方案以根据需要进行更新。