选择具有多个关系的行

Select rows with more than one relationship

提问人:Fabrício Matté 提问时间:1/22/2013 最后编辑:Fabrício Matté 更新时间:1/22/2013 访问量:3044

问:

给定此架构:

CREATE TABLE t (
  id int,
  name varchar(200),
  primary key (id)
);

CREATE TABLE t2 (
  id int,
  name varchar(200),
  primary key (id)
);

CREATE TABLE rel (
  id1 int,
  id2 int,
  value int,
  primary key (id1, id2),
  foreign key (id1) REFERENCES t(id),
  foreign key (id2) REFERENCES t2(id)
);

我应该如何从满足 1 个以上关系的行中查询? 这听起来很容易,但我不确定在这种情况下的最佳实践是什么。我将通过查询更好地解释:tt2

SELECT t.id, t.name
FROM t
INNER JOIN rel ON (t.id = rel.id1)
INNER JOIN t2 ON (t2.id = rel.id2)
WHERE (rel.id2 = 1 AND rel.value = 1)

当我需要找到满足一种关系的行时,上面的内容效果很好。但现在:

SELECT t.id, t.name
FROM t
INNER JOIN rel ON (t.id = rel.id1)
INNER JOIN t2 ON (t2.id = rel.id2)
WHERE (rel.id2 = 1 AND rel.value = 1)
AND (rel.id2 = 2 AND rel.value = 2)

对于 2 个关系,此查询将永远不会起作用,因为联接的行永远不会在一行中有 2 个不同的关系,因此它将始终返回行。0

到目前为止,我一直在使用的解决方案是寻找来自关系的结果集:t.idIN

SELECT t.id, t.name
FROM t
WHERE (t.id IN (SELECT id1 FROM rel WHERE rel.id2 = 1 AND rel.value = 1))
AND (t.id IN (SELECT id1 FROM rel WHERE rel.id2 = 2 AND rel.value = 2))

这可行,但不是有更好的方法吗?我觉得我写了太多的 SQL,对于如此简单的事情,对每个项目做一个子查询似乎有点矫枉过正。

下面是 SQL Fiddle

SQL PostgreSQL

评论


答:

2赞 John Woo 1/22/2013 #1

这称为关系划分。

SELECT   t.id, t.name
FROM     t 
         INNER JOIN rel
            ON t.id = rel.id1
WHERE    rel.id2 IN (1,2) AND
         rel.value = 1
GROUP BY t.id, t.name
HAVING   COUNT(rel.id2) = 2

评论

0赞 Fabrício Matté 1/22/2013
我的问题看起来没问题,但是如果关系具有完全不同的价值观怎么办?这是我的实际用例。
0赞 John Woo 1/22/2013
你能扩展这个更多的关系有完全不同的价值观吗?
0赞 Fabrício Matté 1/22/2013
我将第二个关系的值更改为 2,因此它应该将行与关系匹配t(rel.id2 = 1 AND rel.value = 1) AND (rel.id2 = 2 AND rel.value = 2)
0赞 Fabrício Matté 1/22/2013
+1 我稍后会花时间阅读关系部门的文章。
2赞 GarethD 1/22/2013 #2

我可能过度简化了这个问题,但您能否在 where 子句中将 an 更改为 an 并用于获取给定条件的关系数。例如:ANDORGROUP BY/HAVING

SELECT  t.id, t.name
FROM    t
        INNER JOIN rel 
            ON t.id = rel.id1
        INNER JOIN t2 
            ON t2.id = rel.id2
WHERE   (rel.id2 = 1 AND rel.value = 1)
OR      (rel.id2 = 2 AND rel.value = 1)
GROUP BY t.Id, t.Name
HAVING COUNT(*) = 2;

虽然我会将 where 子句重写为:

WHERE   rel.Value = 1 
AND     rel.ID2 IN (1, 2)

编辑

我不会再用编辑过的问题重写上面的 where 子句。它会变成

WHERE   (rel.id2 = 1 AND rel.value = 1)
OR      (rel.id2 = 2 AND rel.value = 2)

评论

0赞 Fabrício Matté 1/22/2013
哦,没关系,是另一个人发布并删除了答案,但似乎确实可以解决问题。having count(*) > 1