隐性SQL查询

时间:2017-04-02 08:39:09

标签: sql postgresql recursion recursive-query

我有以下关系:

CompanyInfo(公司,角色,员工)

我要做的是找到两个员工之间最短的“路径”。

示例

我需要找到乔和彼得之间的距离。 Joe是A公司的首席执行官,一个名叫Alex的人是董事会成员。 亚历克斯是B公司的首席执行官,彼得是公司B的副总裁。然后,乔和彼得之间的距离将是2.如果乔和彼得在同一家公司担任职务,那就是1。

我需要使用递归SQL来解决这个问题。到目前为止,我已经提出了基本情况和最终选择字符串,但我不能为我的生活找出递归部分。

WITH RECURSIVE shortest_path(c1,p1,c2,p2, path) AS (
  -- Basecase --
  SELECT c1.company, c1.person, c2.company, c2.person, array[c1.person, c2.person]
  FROM CompanyInfo c1
  INNER JOIN CompanyInfo c2 ON c1.company = c2.company
  WHERE c1.person = 'Joe'
  AND c1.person <> c2.person
UNION ALL
  -- Recursive --
  -- This is where I'm stuck.
)

SELECT p1, p2, array_length(path,1) -1 as distance
FROM shortest_path
WHERE p2 = 'Peter'
ORDER BY distance
LIMIT 1;

示例数据

CREATE TABLE CompanyInfo (
  company text,
  role text,
  employee text,
  primary key (company, role, employee)
);

insert into CompanyInfo values('Company A', 'CEO', 'Joe');
insert into CompanyInfo values('Company A', 'Board member', 'Alex');
insert into CompanyInfo values('Company B', 'CEO', 'Alex');
insert into CompanyInfo values('Company B', 'Board member', 'Peter');

预期输出

person 1 | person 2 | distance
Joe        Peter      2

1 个答案:

答案 0 :(得分:1)

试试这个。继续运行直到可以将新员工添加到路径中。

CREATE TABLE CompanyInfo (
  company text,
  role text,
  employee text,
  primary key (company, role, employee)
);

insert into CompanyInfo values('Company A', 'CEO', 'Joe');
insert into CompanyInfo values('Company A', 'Board member', 'Alex');
insert into CompanyInfo values('Company B', 'CEO', 'Alex');
insert into CompanyInfo values('Company B', 'Board member', 'Peter');


WITH RECURSIVE shortest_path(c1,p1,c2,p2, path) AS (
  -- Basecase --
  SELECT c1.company, c1.employee, c2.company, c2.employee, array[c1.employee, c2.employee]
  FROM CompanyInfo c1 
  JOIN CompanyInfo c2 ON c1.company = c2.company
      AND c1.employee = 'Joe'
      AND c1.employee <> c2.employee
  UNION ALL
  -- Recursive --
  SELECT c1, p1, c3.company, c3.employee, path || c3.employee
  FROM shortest_path c1
  JOIN CompanyInfo c2 ON c1.p2 = c2.employee    
  JOIN CompanyInfo c3 ON c3.company = c2.company
      AND NOT c3.employee = ANY (c1.path)
)

SELECT *, array_length(path,1) -1 as distance
FROM shortest_path
WHERE p2 = 'Peter'
ORDER BY distance
LIMIT 1;