创建查询以在“连接”表中查找匹配的对象

时间:2013-04-15 04:07:31

标签: sql postgresql query-optimization jointable

我正在尝试找到一个有效的查询来查找“join”表中的所有匹配对象。

给定一个对象Adopter,其中有许多PetsPets通过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 1Adopter with提出相同的问题,您将获得{{1}}。

如果您再次询问{{1}} id 2'的相同问题,则不会返回任何内容。

我希望这有助于澄清事情!

4 个答案:

答案 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:很高兴你有它的工作!我已经将你的工作示例重构为以下内容:

  • 使用@>运算符。这将检查一个数组是否包含另一个数组,以避免需要显式设置顺序
  • 将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
  );

答案 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)