1

我有一张表大致如下:

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 而不是,然后调用适当的 SELECT 语句。但这似乎非常低效。有没有更好的方法来做到这一点?

4

3 回答 3

0

尝试类似:

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 is not distinct from Btrue如果 A 和 B 相等或两者都相等,则填充返回null

::int意味着cast ( as int)。将布尔值true转换为int将给出1(您不能直接添加布尔值)。

于 2013-07-31T15:12:00.370 回答
0

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

CREATE FUNCTION MostSpecific(BIGINT, BIGINT, BIGINT, BIGINT, BIGINT) 
RETURNS TABLE(f_userid BIGINT, f_groupaid BIGINT, f_groupbid BIGINT, f_groupcid BIGINT, f_itemid BIGINT, f_value TEXT) AS
'WITH cte AS (
  SELECT *, 
    CASE WHEN f_groupaid IS NULL THEN 0 WHEN f_groupaid = $2 THEN 1 END +
    CASE WHEN f_groupbid IS NULL THEN 0 WHEN f_groupbid = $3 THEN 1 END +
    CASE WHEN f_groupcid IS NULL THEN 0 WHEN f_groupcid = $4 THEN 1 END +
    CASE WHEN f_itemid   IS NULL THEN 0 WHEN f_itemid   = $5 THEN 1 END bop 
  FROM t_table
  WHERE f_userid = $1
    AND (f_groupaid IS NULL OR f_groupaid = $2)
    AND (f_groupbid IS NULL OR f_groupbid = $3)
    AND (f_groupcid IS NULL OR f_groupcid = $4)
    AND (f_itemid IS NULL   OR f_itemid   = $5)
)
SELECT f_userid, f_groupaid, f_groupbid, f_groupcid, f_itemid, f_value FROM cte
WHERE bop IS NOT NULL
ORDER BY bop DESC
LIMIT 1'
LANGUAGE SQL
//

一个用于测试的 SQLfiddle

于 2013-07-31T15:20:37.030 回答
0

SQL小提琴

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;
于 2013-07-31T15:34:08.960 回答