Как построить иерархию, используя как можно меньше запросов (один?) - PullRequest
1 голос
/ 24 марта 2019

У меня более новый SQLServer с базой данных с одной таблицей клиентов и одной таблицей со связями (родительские / дочерние отношения). Отношения могут быть разных типов (например, стандартная иерархия компании, друзья, конкуренция и т. Д.).

Я хочу перечислить полную иерархию компании (определенный тип отношений), начиная с одного клиента. Просто полный список, его не нужно заказывать, так как об этом позаботятся снаружи.

Я могу сделать это с помощью своего веб-скрипта, который создаст визуальную часть, но там может быть несколько огромных иерархий, и это приведет к выполнению сотен отдельных запросов. Это может занять слишком много времени.

Я пытался взглянуть на CTE (обычное табличное выражение), но не думаю, что действительно его понимаю. Кажется, я могу перечислить все, где мой начальный клиент является частью, но не там, где он вообще не является частью.

Таблица:

CREATE TABLE CUSTOMERS
(
 id int not null, 
 name varchar(100) not null
);


CREATE TABLE RELATIONSHIPS
(
 relationid int not null,
 relationtype int not null,
 customerid int not null,
 parentid int not null
);

INSERT INTO CUSTOMERS(id,name) VALUES 
(1040,'Cust A'),
(1041,'Cust B'),
(1042,'Cust C'),
(1043,'Cust D'),
(1044,'Cust E'),
(1045,'Cust F'),
(1046,'Cust G'),
(1047,'Cust H');

INSERT INTO RELATIONSHIPS(relationid,relationtype,customerid,parentid)
VALUES
(1,1,1041,1040),
(2,1,1042,1040),
(3,1,1043,1042),
(4,1,1047,1043),
(5,2,1041,1040);

Текущий SQL

with cte as (
    select t.parentid,parent.name as parentname,t.customerid,child.name as childname
    from RELATIONSHIPS t
    INNER JOIN CUSTOMERS parent ON t.parentid = parent.id
    INNER JOIN CUSTOMERS child ON t.customerid = child.id
    where relationtype = 1 and (customerid = 1042 or parentid = 1042)

    union all

    select t.parentid,parent.name as parentname,t.customerid,child.name as childname
    from RELATIONSHIPS t
    INNER JOIN CUSTOMERS parent ON t.parentid = parent.id
    INNER JOIN CUSTOMERS child ON t.customerid = child.id
    inner join cte c on (c.customerid=t.parentid )
    where relationtype = 1
)
select distinct t.* from cte t;

Скрипка: http://sqlfiddle.com/#!18/3e919b/14

Исходя из данных, приведенных выше, я хочу перечислить:

1040,Cust A,1041,Cust B
1040,Cust A,1042,Cust C
1042,Cust C,1041,Cust D
1043,Cust D,1047,Cust H

Мой запрос выше пытается перечислить все дерево иерархии, в которое входит клиент 1042 (Cust C). С моим запросом я, кажется, получаю все, кроме одного для ребенка 1041 (Cust B). Не уверен, как включить такие вещи в запрос, поскольку он не имеет прямого отношения к моей начинающей компании (за исключением того, что он находится в той же иерархии).

1 Ответ

1 голос
/ 24 марта 2019

Боже, это безобразно. Не знаю, лучший ли это исполнитель или нет. Вы фактически говорите, что хотите, чтобы все дети вашего клиента ('Cust C'), все родители вашего клиента, и , а затем все дети этих родителей (которых не было в предыдущем филиале) (а)). Я закончил тем, что сделал это с 3-мя rCTE, что, вероятно, является абсолютным убийцей производительности при большом наборе данных, но, эй, он выполняет свою работу ":"

USE Sandbox;
GO

CREATE TABLE CUSTOMERS
(
 id int not null, 
 name varchar(100) not null
);


CREATE TABLE RELATIONSHIPS
(
 relationid int not null,
 relationtype int not null,
 customerid int not null,
 parentid int not null
);

INSERT INTO CUSTOMERS(id,name) VALUES 
(1040,'Cust A'),
(1041,'Cust B'),
(1042,'Cust C'),
(1043,'Cust D'),
(1044,'Cust E'),
(1045,'Cust F'),
(1046,'Cust G'),
(1047,'Cust H');

INSERT INTO RELATIONSHIPS(relationid,relationtype,customerid,parentid)
VALUES
(1,1,1041,1040),
(2,1,1042,1040),
(3,1,1043,1042),
(4,1,1047,1043),
(5,2,1041,1040);
GO

DECLARE @Customer varchar(100) = 'Cust C';
--Get the children of the Customer
WITH Children AS(
    SELECT Cp.name AS ParentName,
           Cp.Id AS ParentID,
           CC.name AS ChildName,
           Cc.id AS ChildID
    FROM CUSTOMERS Cp
         JOIN RELATIONSHIPS R ON Cp.id = R.parentid
         JOIN CUSTOMERS Cc ON R.customerid = Cc.id
    WHERE Cp.name = @Customer
    UNION ALL
    SELECT C.ChildName AS ParentName,
           C.ChildID AS ParentID,
           Cc.name AS ChildName,
           Cc.Id AS ChildId
    FROM Children C
         JOIN RELATIONSHIPS R ON C.ChildID = r.parentid
         JOIN CUSTOMERS Cc ON R.customerid = Cc.id),
--Get the Parents of the customer
Parents AS(
    SELECT Cp.name AS ParentName,
           Cp.Id AS ParentID,
           CC.name AS ChildName,
           Cc.id AS ChildID
    FROM CUSTOMERS Cc
         JOIN RELATIONSHIPS R ON Cc.id = R.customerid
         JOIN CUSTOMERS Cp ON R.parentid = Cp.id
    WHERE Cc.name = @Customer
    UNION ALL
    SELECT Cp.name AS ParentName,
           Cp.Id AS ParentID,
           P.ParentName AS ChildName,
           P.ParentID AS ChildId
    FROM Parents P
         JOIN RELATIONSHIPS R ON P.ParentID = R.customerid
         JOIN CUSTOMERS Cp ON R.parentid = Cp.id),
--Get the children of the parents. Yuck
ParentChildren AS(
    SELECT DISTINCT
           P.ParentName AS ParentName,
           P.ParentID AS ParentID,
           Cc.name AS ChildName,
           Cc.id AS ChildId
    FROM Parents P
         JOIN RELATIONSHIPS R ON P.ParentID = R.parentid
         JOIN CUSTOMERS Cc ON R.customerid = Cc.id    
    WHERE NOT EXISTS (SELECT 1
                      FROM Parents E
                      WHERE E.ChildID = Cc.id
                        AND E.ParentID = P.ParentID)
    UNION ALL    
    SELECT PC.ChildName AS ParentName,
           PC.ChildId AS ParentID,
           Cc.name AS ChildName,
           Cc.id AS ChildID
    FROM ParentChildren PC
         JOIN RELATIONSHIPS R ON PC.ChildId = R.parentid
         JOIN CUSTOMERS Cc ON R.customerid = Cc.id
    WHERE NOT EXISTS (SELECT 1
                      FROM Parents E
                      WHERE E.ChildID = Cc.id
                        AND E.ParentID = PC.ParentID)
)

SELECT *
FROM Children
UNION ALL
SELECT *
FROM Parents
UNION ALL
SELECT *
FROM ParentChildren
ORDER BY ParentID ASC;

GO

DROP TABLE RELATIONSHIPS;
DROP TABLE CUSTOMERS;
...