【问题标题】:Creating a query to find matching objects in a "join" table创建查询以在“连接”表中查找匹配对象
【发布时间】:2013-04-07 02:45:04
【问题描述】:

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

给定一个对象Adopter,它有很多Pets,以及Pets,它有很多Adopters,通过AdopterPets连接表。我怎样才能找到所有具有相同PetsAdopters

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

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     |
            +------------------+

当您向Adopter1id 询问具有相同Pets 的任何其他Adopters 时,您将被退回id 3

如果您对Adopter3id 提出相同的问题,您会得到id 1

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

我希望这有助于解决问题!

【问题讨论】:

  • “所有Adopters 都具有相同的Pets”是什么意思?和什么一样?另外,what have you tried?
  • 她很清楚地说明了她在代码块下面尝试了什么......
  • Group by 可能会起作用,在应用group by 之前,唯一地 将收养者收养的所有宠物聚合到一行中......比如(adopterid,'firstadoptedpetid.secondadoptedpetid.etc') . 是您可能想要用作连接符的任何字符。我没有说它会很有效,但我很确定这样的东西会起作用。
  • 您的第二次编辑使您的情况看起来像您想要关系代数除法的应用程序(en.wikipedia.org/wiki/Relational_algebra#Division_.28.C3.B7.29)。 IIRC,这些查询当然是可写的,但也不是很容易写。我很想继续努力,但睡眠电话。
  • 我仍然不完全确定您在寻找什么。在您的示例中,如果您向 id 为 2 的采用者提出问题(我假设 adopter_id?),您应该什么也得不到,但会得到 1 3:这两个采用者也都有 pet_id 1。

标签: sql postgresql query-optimization jointable


【解决方案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] 表示。然后,我确保与我进行比较的采用者不包含在结果中。

如果有人看到任何优化,或者是否有办法比较顺序无关紧要的数组,请告诉我。

【讨论】:

  • 好东西!我编辑了我的答案以添加一种处理数组顺序的方法。它可能会被重构更多,希望这会有所帮助。
【解决方案2】:

我不确定这是否正是您正在寻找的,但这可能会给您一些想法。

首先我创建了一些示例数据:

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(*) &gt; 1 子句确保我们只显示拥有共享所有者(超过 1 个)的宠物。如果我们忽略这一点,我们将包括不共享所有者的宠物。

更新

@scommette:很高兴你能成功!我在下面将您的工作示例重构为:

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

【讨论】:

    【解决方案3】:

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

    select pet_id, wm_concat(adopter_id) adopters
    from AdopterPets
    group by pet_id ;
    

    【讨论】:

    【解决方案4】:
    --
    -- 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-23
      • 1970-01-01
      • 2017-07-20
      • 2017-03-24
      • 1970-01-01
      • 2013-08-24
      • 2022-08-18
      • 1970-01-01
      相关资源
      最近更新 更多