1

我试图找到一个有效的查询来查找“连接”表中的所有匹配对象。

Adopter给定一个有 many的对象Pets,并且通过连接表Pets有很多。我怎么能找到所有相同的?AdoptersAdopterPetsAdoptersPets

架构相当规范化,看起来像这样。

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的人时,您将被退回。1AdoptersPetsid 3

如果你问与你相同的问题,Adopter你会得到.id3id 1

如果您再次询问Adopter with id 2` 的相同问题,您将一无所获。

我希望这有助于澄清问题!

4

4 回答 4

1

谢谢大家的帮助,我使用了一些东西的组合:

  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_ids 的顺序是关键,因此主查询中的结果将不依赖于顺序。

在主查询中,我将子查询的结果与我要匹配的采用者的宠物 ID 进行比较。出于此答案的目的,特定采用者的 pet_id 由 [1,2,3] 表示。然后我确保我所比较的采用者不包含在结果中。

让我知道是否有人看到任何优化,或者是否有办法比较顺序无关紧要的数组。

于 2013-04-15T16:08:37.633 回答
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:很高兴你让它工作了!我在下面将您的工作示例重构为:

  • 使用@>运算符。这将检查一个数组是否包含另一个避免需要显式设置顺序
  • 将 grouped_pets 子查询移至 CTE。这不仅是解决方案,而且还巧妙地允许您过滤掉 current_adopter_id 并获取该 id 的宠物

您可能会发现将其包装在一个函数中会很有帮助。

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
  );
于 2013-04-15T04:38:48.943 回答
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)
于 2013-04-16T11:00:49.120 回答
0

如果您使用的是 Oracle,那么wm_concat这里可能会很有用

select pet_id, wm_concat(adopter_id) adopters
from AdopterPets
group by pet_id ;
于 2013-04-15T04:32:13.820 回答