我正在尝试找到一个有效的查询来查找“join”表中的所有匹配对象。
给定一个对象Adopter
,其中有许多Pets
,Pets
通过Adopters
连接表有很多AdopterPets
。如何查找具有相同Adopters
的所有Pets
?
架构相当规范化,看起来像这样。
TABLE Adopter
INTEGER id
TABLE AdopterPets
INTEGER adopter_id
INTEGER pet_id
TABLE Pets
INTEGER id
现在我正在使用的解决方案循环遍历所有Adopters
并随时询问他们的宠物我们有匹配存储它可以在以后使用它,但我相信必须有更好的方法使用它SQL。
我看到的一个SQL解决方案是GROUP BY
,但它似乎不是解决此问题的正确方法。
为了解释我正在寻找的更多内容,我将尝试举例。
+---------+ +------------------+ +------+
| Adptors | | AdptorsPets | | Pets |
|---------| +----------+-------+ |------|
| 1 | |adptor_id | pet_id| | 1 |
| 2 | +------------------+ | 2 |
| 3 | |1 | 1 | | 3 |
+---------+ |2 | 1 | +------+
|1 | 2 |
|3 | 1 |
|3 | 2 |
|2 | 3 |
+------------------+
当您向Adopter
id
1
Adopters
询问具有相同Pets
的任何其他id 3
时,您将被撤回Adopter
}。
如果您使用id
3
id 1
向Adopter with
提出相同的问题,您将获得{{1}}。
如果您再次询问{{1}} id 2'的相同问题,则不会返回任何内容。
我希望这有助于澄清事情!
答案 0 :(得分:1)
我不确定这是否正是您所寻找的,但这可能会给您一些想法。
首先我创建了一些示例数据:
create table adopter (id serial not null primary key, name varchar );
insert into adopter (name) values ('Bob'), ('Sally'), ('John');
create table pets (id serial not null primary key, kind varchar);
insert into pets (kind) values ('Dog'), ('Cat'), ('Rabbit'), ('Snake');
create table adopterpets (adopter_id integer, pet_id integer);
insert into adopterpets values (1, 1), (1, 2), (2, 1), (2,3), (2,4), (3, 1), (3,3);
接下来我运行了这个查询:
SELECT p.kind, array_agg(a.name) AS adopters
FROM pets p
JOIN adopterpets ap ON ap.pet_id = p.id
JOIN adopter a ON a.id = ap.adopter_id
GROUP BY p.kind
HAVING count(*) > 1
ORDER BY kind;
kind | adopters
--------+------------------
Dog | {Bob,Sally,John}
Rabbit | {Sally,John}
(2 rows)
在这个例子中,对于每个宠物,我正在创建一个包含所有所有者的数组。 HAVING count(*) > 1
子句确保我们只显示共享所有者(超过1个)的宠物。如果我们将其删除,我们将包括不与所有者共享的宠物。
<强>更新强>
@scommette:很高兴你有它的工作!我已经将你的工作示例重构为以下内容:
@>
运算符。这将检查一个数组是否包含另一个数组,以避免需要显式设置顺序您可能会发现将它包装在函数中会很有帮助。
WITH grouped_pets AS (
SELECT adopter_id, array_agg(pet_id ORDER BY pet_id) AS pets
FROM adopters_pets
GROUP BY adopter_id
)
SELECT * FROM grouped_pets
WHERE adopter_id <> 3
AND pets @> (
SELECT pets FROM grouped_pets WHERE adopter_id = 3
);
答案 1 :(得分:0)
如果你使用的是Oracle,那么wm_concat
在这里很有用
select pet_id, wm_concat(adopter_id) adopters
from AdopterPets
group by pet_id ;
答案 2 :(得分:0)
谢谢大家的帮助,我结合使用了一些东西:
SELECT adopter_id
FROM (
SELECT adopter_id, array_agg(pet_id ORDER BY pet_id)
AS pets
FROM adopters_pets
GROUP BY adopter_id
) AS grouped_pets
WHERE pets = array[1,2,3] #array must be ordered
AND adopter_id <> current_adopter_id;
在子查询中,我将pet_ids按其采用者分组。 pet_id的顺序是关键,因此主查询中的结果将不依赖于顺序。
在主查询中,我将子查询的结果与我希望匹配的采用者的宠物ID进行比较。出于本答案的目的,特定采用者的pet_id由[1,2,3]表示。然后我确保我所比较的采用者不包含在结果中。
让我知道是否有人看到任何优化,或者是否有办法比较顺序无关紧要的数组。
答案 3 :(得分:0)
--
-- Relational division 1.0
-- Show all people who own *exactly* the same (non-empty) set
-- of animals as I do.
--
-- Test data
CREATE TABLE adopter (id INTEGER NOT NULL primary key, fname varchar );
INSERT INTO adopter (id,fname) VALUES (1,'Bob'), (2,'Alice'), (3,'Chris');
CREATE TABLE pets (id INTEGER NOT NULL primary key, kind varchar);
INSERT INTO pets (id,kind) VALUES (1,'Dog'), (2,'Cat'), (3,'Pig');
CREATE TABLE adopterpets (adopter_id integer REFERENCES adopter(id)
, pet_id integer REFERENCES pets(id)
);
INSERT INTO adopterpets (adopter_id,pet_id) VALUES (1, 1), (1, 2), (2, 1), (2,3), (3,1), (3,2);
-- Show it to the world
SELECT ap.adopter_id, ap.pet_id
, a.fname, p.kind
FROM adopterpets ap
JOIN adopter a ON a.id = ap.adopter_id
JOIN pets p ON p.id = ap.pet_id
ORDER BY ap.adopter_id,ap.pet_id;
SELECT DISTINCT other.fname AS same_as_me
FROM adopter other
-- moi has *at least* one same kind of animal as toi
WHERE EXISTS (
SELECT * FROM adopterpets moi
JOIN adopterpets toi ON moi.pet_id = toi.pet_id
WHERE toi.adopter_id = other.id
AND moi.adopter_id <> toi.adopter_id
-- C'est moi!
AND moi.adopter_id = 1 -- 'Bob'
-- But moi should not own an animal that toi doesn't have
AND NOT EXISTS (
SELECT * FROM adopterpets lnx
WHERE lnx.adopter_id = moi.adopter_id
AND NOT EXISTS (
SELECT *
FROM adopterpets lnx2
WHERE lnx2.adopter_id = toi.adopter_id
AND lnx2.pet_id = lnx.pet_id
)
)
-- ... And toi should not own an animal that moi doesn't have
AND NOT EXISTS (
SELECT * FROM adopterpets rnx
WHERE rnx.adopter_id = toi.adopter_id
AND NOT EXISTS (
SELECT *
FROM adopterpets rnx2
WHERE rnx2.adopter_id = moi.adopter_id
AND rnx2.pet_id = rnx.pet_id
)
)
)
;
结果:
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "adopter_pkey" for table "adopter"
CREATE TABLE
INSERT 0 3
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pets_pkey" for table "pets"
CREATE TABLE
INSERT 0 3
CREATE TABLE
INSERT 0 6
adopter_id | pet_id | fname | kind
------------+--------+-------+------
1 | 1 | Bob | Dog
1 | 2 | Bob | Cat
2 | 1 | Alice | Dog
2 | 3 | Alice | Pig
3 | 1 | Chris | Dog
3 | 2 | Chris | Cat
(6 rows)
same_as_me
------------
Chris
(1 row)