我有一组表定义了一些需要遵循的规则,例如:
CREATE TABLE foo.subrules (
subruleid SERIAL PRIMARY KEY,
ruleid INTEGER REFERENCES foo.rules(ruleid),
subrule INTEGER,
barid INTEGER REFERENCES foo.bars(barid)
);
INSERT INTO foo.subrules(ruleid,subrule,barid) VALUES
(1,1,1),
(1,1,2),
(1,2,2),
(1,2,3),
(1,2,4),
(1,3,3),
(1,3,4),
(1,3,5),
(1,3,6),
(1,3,7);
这定义的是一组"子规则"需要满足......如果所有"子规则"如果满意,那么规则也会得到满足。
在上面的例子中," subruleid" 1
可以满足于" barid"值1
或2
。
另外," subruleid" 2
可以满足于" barid" 2
,3
或4
的值。
同样," subruleid" 3
可以满足于" barid"值3
,4
,5
,6
或7
。
我还有一个如下所示的数据集:
primarykey | resource | barid
------------|------------|------------
1 | A | 1
2 | B | 2
3 | C | 8
棘手的部分是,一旦" subrule"对资源"满意,#34;资源"不能满足任何其他"子规则" (即使相同的" barid"会满足另一个" subrule")
所以,我需要的是评估并返回以下结果:
ruleid | subrule | barid | primarykey | resource
------------|------------|------------|------------|------------
1 | 1 | 1 | 1 | A
1 | 1 | 2 | NULL | NULL
1 | 2 | 2 | 2 | B
1 | 2 | 3 | NULL | NULL
1 | 2 | 4 | NULL | NULL
1 | 3 | 3 | NULL | NULL
1 | 3 | 4 | NULL | NULL
1 | 3 | 5 | NULL | NULL
1 | 3 | 6 | NULL | NULL
1 | 3 | 7 | NULL | NULL
NULL | NULL | NULL | 3 | C
有趣的是,如果" primarykey" 3
有一个" barid"值2
(而非8
)的结果将相同。
我尝试了几种方法,包括plpgsql
函数,通过" subruleid"执行分组。使用ARRAY_AGG(barid)
并从barid
构建一个数组,并检查barid
数组中的每个元素是否在" subruleid"通过循环分组,但它感觉不对。
是否有更优雅或更有效的选择?
答案 0 :(得分:2)
以下片段找到解决方案(如果有)。第三(资源)是硬编码的。如果只需要一个解决方案,则应添加一些对称断路器。
如果资源的数量没有限制,我认为可以通过列举所有可能的表格(Hilbert?mixed-radix?)并在修剪之后选择它们来解决问题。令人满意的。
-- the data
CREATE TABLE subrules
( subruleid SERIAL PRIMARY KEY
, ruleid INTEGER -- REFERENCES foo.rules(ruleid),
, subrule INTEGER
, barid INTEGER -- REFERENCES foo.bars(barid)
);
INSERT INTO subrules(ruleid,subrule,barid) VALUES
(1,1,1), (1,1,2),
(1,2,2), (1,2,3), (1,2,4),
(1,3,3), (1,3,4), (1,3,5), (1,3,6), (1,3,7);
CREATE TABLE resources
( primarykey INTEGER NOT NULL PRIMARY KEY
, resrc varchar
, barid INTEGER NOT NULL
);
INSERT INTO resources(primarykey,resrc,barid) VALUES
(1, 'A', 1) ,(2, 'B', 2) ,(3, 'C', 8)
-- ################################
-- uncomment next line to find a (two!) solution(s)
-- ,(4, 'D', 7)
;
-- all matching pairs of subrules <--> resources
WITH pairs AS (
SELECT sr.subruleid, sr.ruleid, sr.subrule, sr.barid
, re.primarykey, re.resrc
FROM subrules sr
JOIN resources re ON re.barid = sr.barid
)
SELECT
p1.ruleid AS ru1 , p1.subrule AS sr1 , p1.resrc AS one
, p2.ruleid AS ru2 , p2.subrule AS sr2 , p2.resrc AS two
, p3.ruleid AS ru3 , p3.subrule AS sr3 , p3.resrc AS three
-- self-join the pairs, excluding the ones that
-- use the same subrule or resource
FROM pairs p1
JOIN pairs p2 ON p2.primarykey > p1.primarykey -- tie-breaker
JOIN pairs p3 ON p3.primarykey > p2.primarykey -- tie breaker
WHERE 1=1
AND p2.subruleid <> p1.subruleid
AND p2.subruleid <> p3.subruleid
AND p3.subruleid <> p1.subruleid
;
结果(取消注释缺少资源的行):
ru1 | sr1 | one | ru2 | sr2 | two | ru3 | sr3 | three
-----+-----+-----+-----+-----+-----+-----+-----+-------
1 | 1 | A | 1 | 1 | B | 1 | 3 | D
1 | 1 | A | 1 | 2 | B | 1 | 3 | D
(2 rows)
资源{A,B,C}当然可以是硬编码的,但这会阻止“D”记录(或任何其他记录)作为缺失的链接。
答案 1 :(得分:1)
既然你没有澄清这个问题,我会按照我自己的假设。
subrule
数字按升序排列,每条规则都没有间隙。(subrule, barid)
中的UNIQUE
为subrules
。barid
有多个资源,则这些对等项中的分配是任意的。算法如下:
subrule
个数字的子规则。barid
(第一个具有匹配资源的资源),这会消耗资源。subruleid
并重复2. 您可以使用递归CTE 纯SQL 实现此功能:
WITH RECURSIVE cte AS ((
SELECT s.*, r.resourceid, r.resource
, CASE WHEN r.resourceid IS NULL THEN '{}'::int[]
ELSE ARRAY[r.resourceid] END AS consumed
FROM subrules s
LEFT JOIN resource r USING (barid)
WHERE s.ruleid = 1
ORDER BY s.subrule, r.barid, s.barid
LIMIT 1
)
UNION ALL (
SELECT s.*, r.resourceid, r.resource
, CASE WHEN r.resourceid IS NULL THEN c.consumed
ELSE c.consumed || r.resourceid END
FROM cte c
JOIN subrules s ON s.subrule = c.subrule + 1
LEFT JOIN resource r ON r.barid = s.barid
AND r.resourceid <> ALL (c.consumed)
ORDER BY r.barid, s.barid
LIMIT 1
))
SELECT ruleid, subrule, barid, resourceid, resource FROM cte
UNION ALL -- add unused rules
SELECT s.ruleid, s.subrule, s.barid, NULL, NULL
FROM subrules s
LEFT JOIN cte c USING (subruleid)
WHERE c.subruleid IS NULL
UNION ALL -- add unused resources
SELECT NULL, NULL, r.barid, r.resourceid, r.resource
FROM resource r
LEFT JOIN cte c USING (resourceid)
WHERE c.resourceid IS NULL
ORDER BY subrule, barid, resourceid;
返回 完全 您要求的结果 SQL Fiddle.
它基本上是上面列出的算法的实现。
每barid
个subrule
只能进行一次匹配。因此LIMIT 1
需要额外的括号:
收集&#34;消费&#34;数组consumed
中的资源,并将其排除在r.resourceid <> ALL (c.consumed)
的重复分配之外。特别注意我如何避免数组中的NULL值,这会破坏测试。
CTE仅返回匹配的行。在外部SELECT
中添加不匹配的规则和资源,以获得完整的结果。
或者你在表subrule
和resource
上打开两个游标,并用任何不错的编程语言(包括PL / pgSQL)实现算法。