【问题标题】:Postgres query to fetch both masters and slavesPostgres 查询以获取主服务器和从服务器
【发布时间】:2016-09-29 23:33:53
【问题描述】:

可能标题不好,抱歉。 情况是这样的:

用户表:

id
name

连接表:

id
master_id
slave_id


insert into Users values(1,'Jack');
insert into Users values(2,'Marc');
insert into Users values(3,'Susie');
insert into Users values(4,'Paul');

insert into Connections values(1,1,2);
insert into Connections values(2,3,1);
insert into Connections values(3,3,4);

通过上述插入,杰克将马克作为奴隶。 但他也是苏西的奴隶。 连保罗都是苏西的奴隶。

现在我需要把所有杰克的奴隶都带回来。但我还需要获取杰克的主人和那个主人的奴隶。

在英语中,这意味着对于杰克,我将获得表中的所有用户。因为马克是杰克的奴隶。苏西是杰克的师父。 Paul 是 Susie 的奴隶(Susie 是 Jack 的主人,所以 Paul 在我的用户列表中以某种方式也属于我)。

希望这很清楚。

是否有可能在一个查询中获得所有这些信息? 现在有一个查询获取所有 Jack 的奴隶。我建造了一个来获得所有杰克的主人。但后来我需要循环每个主人来获取他所有的奴隶。所有这些都会产生至少 3 个查询。 由于我使用 node.js 和所有回调的东西,它不是一个很好的选择。 我在考虑 UNION 但我不确定这是要走的路。还有存储过程,我宁愿避免它们。

实际上,使用 UNION 我可以选择我从属的行和我主控的行。但我仍然不知道如何获取我的主人是 master_id 的那些行。

---编辑---

我现在正在运行这个查询:

select
"connections"."master_id"
,"connections"."slave_id"
from
"connections"
where
"connections"."master_id" = 1
union
select
"connections"."master_id"
,"connections"."slave_id"
from
"connections"
where
"connections"."slave_id" = 1    
union
select
"connections"."master_id"
,"connections"."slave_id"
from
"connections"
where
"connections"."master_id" IN 
(select "connections"."master_id" from "connections" where "connections"."slave_id" = 1)        

它似乎给了我预期的结果。 在这种情况下,这将是表连接中的所有行。 你认为它看起来正确吗?

【问题讨论】:

  • 您期望从样本数据中得到什么输出?
  • 我编辑了这个问题,甚至添加了我认为可能的解决方案。

标签: sql postgresql


【解决方案1】:

这是递归 CTE 非常有用的场景类型。递归 CTE 是指回自身的特殊 CTE。我们使用它来遍历层次结构,而不是进行一堆自连接,当层次结构的深度在其不同的路径上随时间流动时,这通常是一个不好的选择。

WITH RECURSIVE recCTE() AS
(
    /*Recursive Seed - The start of the recursive lookup*/
    SELECT
        master_id as parent,
        slave_id as child,
        /*You can use "depth" to check how deep we are in the master/slave hierarchy*/
        1 as Depth,
        /*You can use a "path" to see which people/nodes are involved in the hierarchy as it's built through the iterations of the recursive CTE*/
        CAST(master_id || '>' || child as VARCHAR(50)) as path
    FROM
        Connections
    WHERE
        /* here we determine who we are starting with for the lookup. You could start with everyone by omitting this*/
        /* We'll start with Susie */
        master_id = 3


    /*
        Recursive Term - The part of the query that refers to itself and iterates until
        the inner join fails
    */
    SELECT
        recCTE.child as parent,
        connections.slave_id as child,
        recCTE.depth + 1 as depth,
        recCTE.path || '>' || connections.slave_id as path
    FROM
        recCTE /*referred to itself here*/
        INNER JOIN connections ON
            recCTE.child = connections.master_id /*Join child to master for next lookup of slave/child */
    WHERE
        /*safe guard in case of endless cycling (A reporting to B reporting to C reporting back to A)*/
        recCTE.Depth < 15 

        /*besides checking for depth, you could also insure that the slave doesn't exist in the path already*/
        recCTE.path NOT LIKE '%' || slave_id || '%'


)

/*Now select from it and see what you get*/
SELECT * FROM recCTE;

Check out the official Postgres documentation on Recursive CTEs here

【讨论】:

  • 拜托,你能看看编辑过的问题吗?我想出了一个似乎给我预期结果的解决方案。但我不完全确定它是最好的。
  • 你的可以正常工作(就像单个查询中的自连接一样),但是一旦你的层次结构超过 1 或 2 层,你最终会得到一个联合查询的地狱。为了规模,您应该考虑递归。这样,如果 Susie 是 Jack 的主人,而 Paul 是 Marc 的主人,您可以为 Susie 运行此命令并在输出中获取 Marc。
  • 但是我当然理解缩放的关注,并且会对此进行调查。
  • 你的深度不会超过 3。它将获取主记录、从属记录和从属从属记录。您将不得不为您想要达到的每个深度级别不断添加。但是.. 要么很多 LEFT JOIN 回到同一个表,要么带有相关子查询的 UNION 将是你唯一的选择,如果你不能做递归 CTE。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-25
相关资源
最近更新 更多