Запрос победителя статуса самосоединения SQL

#sql-server #hierarchy #self-join

#sql-сервер #иерархия #самосоединение

Вопрос:

У меня есть таблица клиентов с самосоединением (родительско-дочерний элемент), мне нужно написать запрос, который возвращает дочерних клиентов, статус которых позволяет родителю или дочернему элементу разместить заказ. Столбец является битовым столбцом и имеет значение null.

Возвращаемые результаты будут основаны на следующей матрице:

 parent_status   child_status    Child is allowed to Order
null                 null           FALSE
null                 0              FALSE
null                 1              TRUE
1                    null           TRUE
1                    1              TRUE
1                    0              FALSE
0                    null           FALSE
0                    1              FALSE
0                    0              FALSE
  

в соответствии с запросом здесь приведена схема и сценарий для данных

     CREATE TABLE [dbo].[Customer](
    [Customer_id] [int] NOT NULL,
    [ParentCustomer_id] [int] NULL,
    [Name_desc] [nvarchar](50) NULL,
    [OrderIsAllowed_status] [bit] NULL)
GO
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(1,null,'Parent 1',1)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(2,1,'Parent 1 - Child 1',null)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(3,1,'Parent 1 - Child 2',0)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(4,1,'Parent 1 - Child 3',1)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(5,null,'Parent 2',null)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(6,5,'Parent 2 - Child 1',null)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(7,5,'Parent 2 - Child 2',1)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(8,5,'Parent 2 - Child 3',0)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(9,null,'Parent 3',0)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(10,9,'Parent 3 - Child 1',null)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(11,9,'Parent 3 - Child 2',1)
insert Customer
([Customer_id],[ParentCustomer_id],[Name_desc] ,[OrderIsAllowed_status])
values(12,9,'Parent 3 - Child 3',0)
  

Комментарии:

1. Пожалуйста, добавьте образец структуры данных, который у вас есть. Что вы пробовали до сих пор?

2. для оказания помощи предоставлено недостаточно информации. Пожалуйста, предоставьте дополнительные образцы данных / схемы

3. извинения добавлены схема и некоторые образцы данных — просто подумал, что это достаточно простая схема, ее предоставление казалось ненужным

4. Каков ваш ожидаемый результат?

Ответ №1:

На основе таблицы истинности, СЛУЧАЙ, КОГДА для него будет что-то вроде приведенного ниже.

В примере используется табличная переменная только для демонстрации.

 declare @Customer table (Customer_id int NOT NULL, ParentCustomer_id int, OrderIsAllowed_status bit);

insert @Customer ([Customer_id], [ParentCustomer_id], [OrderIsAllowed_status]) values
(1,null,1),
(2,1,null),
(3,1,0),
(4,1,1),
(5,null,null),
(6,5,null),
(7,5,1),
(8,5,0),
(9,null,0),
(10,9,null),
(11,9,1),
(12,9,0);

select 
child.Customer_id, 
child.ParentCustomer_id,
(case 
 when child.ParentCustomer_id is null then 'Parent '  cast(child.Customer_id as varchar)
 else concat('Parent ',parent.Customer_id,' - Child ',child.Customer_id)
 end) as Name_desc,
parent.OrderIsAllowed_status as parent_status,
child.OrderIsAllowed_status as child_status,
cast(case 
     when child.OrderIsAllowed_status = 1 and parent.OrderIsAllowed_status = 1 then 1
     when child.OrderIsAllowed_status = 1 and parent.OrderIsAllowed_status is null then 1
     when child.OrderIsAllowed_status is null and parent.OrderIsAllowed_status = 1 then 1
     else 0 
     end as bit) as [Child is allowed to Order]
from @Customer child
left join @Customer parent on (child.ParentCustomer_id = parent.Customer_id);
  

Комментарии:

1. Спасибо за ответ: я попытался ответить на свой собственный вопрос… что было бы неправильно со следующим утверждением ..выберите p.Customer_id , p.Name_desc ,p.OrderIsAllowed_status как Parent_IsAllowed , c.Customer_id , c.ParentCustomer_id ,c.Name_desc , c.OrderIsAllowed_status от клиента p внутреннее объединение клиента c на p.customer_id = c.ParentCustomer_id где (p.OrderIsAllowed_status равенnull и c.OrderIsAllowed_status =1) или (p.OrderIsAllowed_status = 1 и c.OrderIsAllowed_status равно null) или (p.OrderIsAllowed_status = 1 и c.OrderIsAllowed_status = 1)

2. Если цель этого SQL состоит в том, чтобы получить только те дочерние элементы, в которых [дочернему элементу разрешено заказывать] верно, то этот SQL не ошибочен. Хотя лично с отношением «многие к одному» я бы поместил «много» в from и присоединил к нему «один».

Ответ №2:

Вы можете использовать рекурсивный CTE:

 ;WITH rec AS (
    SELECT  Customer_id,
            ParentCustomer_id,
            Name_desc,
            OrderIsAllowed_status,
            CAST(NULL AS bit) as parent_status,
            1 as [Level]
    FROM #Customer c
    WHERE ParentCustomer_id IS NULL
    UNION ALL
    SELECT  c.Customer_id,
            c.ParentCustomer_id,
            c.Name_desc,
            c.OrderIsAllowed_status,
            r.OrderIsAllowed_status,
            r.[Level]  1
    FROM rec r
    INNER JOIN #Customer c
        ON c.ParentCustomer_id = r.Customer_id
)

SELECT  r.Customer_id,
        r.ParentCustomer_id,
        r.Name_desc,
        r.OrderIsAllowed_status,
        rs.[Child is allowed to Order]
FROM rec r
INNER JOIN #rules rs
    ON COALESCE(r.[OrderIsAllowed_status],2) = COALESCE(rs.child_status,2)
        AND COALESCE(r.parent_status,2) = COALESCE(rs.parent_status,2)
WHERE r.ParentCustomer_id IS NOT NULL
  

Вывод:

 Customer_id ParentCustomer_id   Name_desc           OrderIsAllowed_status   Child is allowed to Order
10          9                   Parent 3 - Child 1  NULL                    FALSE
11          9                   Parent 3 - Child 2  1                       FALSE
12          9                   Parent 3 - Child 3  0                       FALSE
6           5                   Parent 2 - Child 1  NULL                    FALSE
7           5                   Parent 2 - Child 2  1                       TRUE
8           5                   Parent 2 - Child 3  0                       FALSE
2           1                   Parent 1 - Child 1  NULL                    TRUE
3           1                   Parent 1 - Child 2  0                       FALSE
4           1                   Parent 1 - Child 3  1                       TRUE
  

Я использовал эти таблицы:

 CREATE TABLE #Customer (
    [Customer_id] [int] NOT NULL,
    [ParentCustomer_id] [int] NULL,
    [Name_desc] [nvarchar](50) NULL,
    [OrderIsAllowed_status] [bit] NULL
)

INSERT INTO #Customer VALUES
(1,null,'Parent 1',1),
(2,1,'Parent 1 - Child 1',null),
(3,1,'Parent 1 - Child 2',0),
(4,1,'Parent 1 - Child 3',1),
(5,null,'Parent 2',null),
(6,5,'Parent 2 - Child 1',null),
(7,5,'Parent 2 - Child 2',1),
(8,5,'Parent 2 - Child 3',0),
(9,null,'Parent 3',0),
(10,9,'Parent 3 - Child 1',null),
(11,9,'Parent 3 - Child 2',1),
(12,9,'Parent 3 - Child 3',0)

CREATE TABLE #rules (
    parent_status bit NULL,
    child_status bit NULL,
    [Child is allowed to Order] nvarchar(5) NULL
)

INSERT INTO #rules VALUES
(null, null, 'FALSE'),
(null, 0, 'FALSE'),
(null, 1, 'TRUE'),
(1, null, 'TRUE'),
(1, 1, 'TRUE'),
(1, 0, 'FALSE'),
(0, null, 'FALSE'),
(0, 1, 'FALSE'),
(0, 0, 'FALSE')
  

Ответ №3:

Я попытался ответить на свой собственный вопрос…что было бы неправильно в следующем…если бы я хотел вернуть только дочерних клиентов, которым было разрешено разместить заказ:

  select p.Customer_id
    ,p.Name_desc
    ,p.OrderIsAllowed_status as Parent_IsAllowed
    ,c.Customer_id
    ,c.ParentCustomer_id
    ,c.Name_desc
    ,c.OrderIsAllowed_status    
from Customer p
inner join Customer c
    on p.customer_id = c.ParentCustomer_id
where 
    (p.OrderIsAllowed_status is null and c.OrderIsAllowed_status =1)
    or(p.OrderIsAllowed_status = 1 and c.OrderIsAllowed_status is null)
    or (p.OrderIsAllowed_status = 1 and c.OrderIsAllowed_status = 1)
  

Комментарии:

1. Это не сработает, если у вас есть grandparent - parent - child .

2. Спасибо — я пропустил этот сценарий .. историческое использование таблицы в моем случае имеет только один уровень иерархии — родительский — дочерний, но вы правы, мне нужно рассмотреть оригинальный дизайн

3. С удовольствием! 🙂