Postgresql 高效地从表中选择最具体的结果

Postgresql 高效地从表中选择最具体的结果,postgresql,Postgresql,我的表格大致如下: CREATE TABLE t_table ( f_userid BIGINT NOT NULL ,f_groupaid BIGINT ,f_groupbid BIGINT ,f_groupcid BIGINT ,f_itemid BIGINT ,f_value TEXT ); 这些组是正交的,因此除了表中的每个条目都有一个用户ID这一事实之外,不能暗示任何层次结构。任何列中都

我的表格大致如下:

CREATE TABLE t_table (
    f_userid       BIGINT NOT NULL
   ,f_groupaid     BIGINT
   ,f_groupbid     BIGINT
   ,f_groupcid     BIGINT
   ,f_itemid       BIGINT
   ,f_value        TEXT
);
这些组是正交的,因此除了表中的每个条目都有一个用户ID这一事实之外,不能暗示任何层次结构。任何列中都没有唯一性

例如,一个简单的设置可能是:

INSERT INTO t_table VALUES (1, NULL, NULL, NULL, NULL, 'Value for anything by user 1');
INSERT INTO t_table VALUES (1,    5,    2, NULL, NULL, 'Value for anything by user 1 in groupA 5 groupB 2');
INSERT INTO t_table VALUES (1,    4, NULL,    1, NULL, 'Value for anything by user 1  in groupA 5 and groupC 1');
INSERT INTO t_table VALUES (2, NULL, NULL, NULL, NULL, 'Value for anything by user 2');
INSERT INTO t_table VALUES (2,    1, NULL, NULL, NULL, 'Value for anything by user 2 in groupA 1');
INSERT INTO t_table VALUES (2,    1,    3,    4,    5, 'Value for item 5 by user 2 in groupA 1 and groupB 3 and groupC 4');
对于任何给定的user/groupA/groupB/groupC/item集合,我希望能够获得表中适用的最具体的项。如果给定集合中的任何一个为NULL,那么它只能匹配表中包含NULL的相关列。例如:

// Exact match
SELECT MostSpecific(1, NULL, NULL, NULL, NULL) => "Value for anything by user 1" 
// Match the second entry because groupC and item were not specified in the table and the other items matched
SELECT MostSpecific(1, 5, 2, 3, NULL) => "Value for anything by user 1 in groupA 5 groupB 2"
// Does not match the second entry because groupA is NULL in the query and set in the table
SELECT MostSpecific(1, NULL, 2, 3, 4) => "Value for anything by user 1"
这里最明显的方法是让存储过程遍历参数,找出哪些是NULL,哪些不是NULL,然后调用适当的SELECT语句。但这似乎效率很低。有更好的方法吗?

试试以下方法:

select *
from t_table t

where f_userid = $p_userid
  and (t.f_groupaid is not distinct from $p_groupaid or t.f_groupaid is null) --null in f_groupaid matches both null and not null values
  and (t.f_groupbid is not distinct from $p_groupbid or t.f_groupbid is null)
  and (t.f_groupcid is not distinct from $p_groupcid or t.f_groupcid is null)

order by (t.f_groupaid is not distinct from $p_groupaid)::int -- order by count of matches
        +(t.f_groupbid is not distinct from $p_groupbid)::int
        +(t.f_groupcid is not distinct from $p_groupcid)::int desc
limit 1;
它将为您提供小组赛中的最佳比赛

如果A和B相等或都为null,则A与B填充返回true不不同


::int表示强制转换为int。将布尔值强制转换为true将得到1。您不能直接添加布尔值。

应该可以做到这一点,只需使用WHERE筛选出任何不匹配的行,然后根据其匹配程度对其余行进行排序。如果有任何列不匹配,整个bop表达式将导致NULL,因此我们在外部查询中过滤掉它,在外部查询中我们也按匹配排序,并将结果限制为仅一个最佳匹配

创建函数mostSpecificBrigint,BIGINT,BIGINT,BIGINT,BIGINT 返回TABLEf_userid BIGINT、f_groupaid BIGINT、f_groupbid BIGINT、f_groupcid BIGINT、f_itemid BIGINT、f_value TEXT作为 “以cte为例 选择*, 当f_groupaid为NULL时为0,当f_groupaid=$2时为1结束+ 当f_groupbid为空时为0,当f_groupbid=$3时为1结束+ 当f_groupcid为NULL时为0,当f_groupcid=$4时为1结束+ 如果f_itemid为NULL,则当f_itemid=$5时为0,则为1 从t_表 其中f_userid=$1 f_groupaid为NULL或f_groupaid=$2 f_groupbid为空或f_groupbid=$3 f_groupcid为NULL或f_groupcid=$4 f_itemid为NULL或f_itemid=$5 从cte中选择f_userid、f_groupaid、f_groupbid、f_groupcid、f_itemid、f_value 其中bop不为空 由bop DESC订购 限制1' 语言SQL //


如果参数在请求中为null,那么在响应中必须为null?如果请求中的参数不为null,则该参数必须为null或响应中的指定值?如果请求中的参数为null,则该参数只能与该值为null的行匹配。如果该参数在请求中不为NULL,则它可以匹配NULL或指定值,并优先选择具有指定值的行。这将始终执行完整表扫描。无需返回setof。此函数将永远不会返回超过一行。@Igor Ok,已更改。感谢您的回复。由于这三个答案略有不同,我将构建一些大规模测试,并在给出答案之前看看哪个性能最好。正如@Joachim所指出的,这会对每个函数运行一个完整的表扫描,因此比其他答案花费的时间要长得多。但是再次感谢你的回复,谢谢你的回复。由于这三个答案略有不同,我将构建一些大规模测试,并在给出答案之前查看哪一个性能最好。是的,在性能方面,使用真实数据集进行测试是最重要的:很好的解决方案,非常清楚它是如何工作的,也是最快的性能。谢谢谢谢你的回复。由于这三个答案略有不同,我将进行一些大规模测试,看看哪一个表现最好,然后再给出答案。这与我选择的答案的表现类似,但在所有合理的指标都落实到位后,运行速度要慢25%。
create or replace function mostSpecific(
    p_userid bigint,
    p_groupaid bigint,
    p_groupbid bigint,
    p_groupcid bigint,
    p_itemid bigint
) returns t_table as $body$

select *
from t_table
order by
    (p_userid is not distinct from f_userid or f_userid is null)::integer
    +
    (p_groupaid is not distinct from f_groupaid or f_userid is null)::integer
    +
    (p_groupbid is not distinct from f_groupbid or f_userid is null)::integer
    +
    (p_groupcid is not distinct from f_groupcid or f_userid is null)::integer
    +
    (p_itemid is not distinct from f_itemid or f_userid is null)::integer
    desc
limit 1
;
$body$ language sql;