Как создать кластер связанных записей в отношении «многие ко многим»?

#sql #many-to-many #recursive-query

#sql #многие ко многим #рекурсивный запрос

Вопрос:

У меня есть таблица пользователей подписки с идентификатором контакта и идентификатором заказа. Несколько контактов могут быть связаны с одним заказом, а контакт может быть связан с несколькими заказами. Я пытаюсь взять данный заказ, просмотреть пользователей для этого заказа, определить любые другие заказы, с которыми связан любой из этих пользователей, и связать их как одну компанию, как показано в таблице:

изображение желаемого результата

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

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

2. SELECT DISTINCT contactID FROM a WHERE orderID IN (SELECT orderID FROM a WHERE contactID IN (SELECT contactID FROM a WHERE orderID = 5))

3. Спасибо за вашу помощь с этим Jere

Ответ №1:

По-видимому, я был неправ, и SQL предоставляет способ решить вашу проблему. Вот мое решение. Он не оптимизирован с точки зрения эффективности времени выполнения — если это необходимо, я мог бы взглянуть на него еще раз:

 with recursive 
    incompany(contact, order1, order2) 
        as (select contact, o1.orderID as order1, o2.orderID as order2 
            from orders o1 join orders o2 using (contact) 
            union 
            select o1.contact, inc.order1, o2.orderID as order2 
            from incompany as inc, orders as o1, orders as o2 
            where inc.order2=o1.orderID and o1.contact=o2.contact) 

select contact, sum(order1) as MyNewCompanyID from 
    (select distinct contact, order1 from incompany) as foo 
    group by contact;
  

В первой части я определяю рекурсивный запрос incompany , который выполняет большую часть работы и присваивает этому контакту все orderID , которые используются другим контактом в той же компании. Так select * from incompany; само по себе возвращало бы следующую таблицу:

  --------- -------- -------- 
| contact | order1 | order2 |
 --------- -------- -------- 
| a       |      1 |      1 |
| a       |      1 |      2 |
| a       |      2 |      1 |
| a       |      2 |      2 |
| a       |      3 |      1 |
| a       |      3 |      2 |
| b       |      1 |      1 |
| b       |      2 |      1 |
| b       |      3 |      1 |
| c       |      1 |      1 |
| c       |      2 |      1 |
| c       |      3 |      1 |
| d       |      1 |      2 |
| d       |      1 |      3 |
| d       |      2 |      2 |
| d       |      2 |      3 |
| d       |      3 |      2 |
| d       |      3 |      3 |
| e       |      4 |      4 |
| e       |      4 |      5 |
| e       |      5 |      4 |
| e       |      5 |      5 |
| f       |      4 |      4 |
| f       |      5 |      4 |
| g       |      4 |      5 |
| g       |      5 |      5 |
 --------- -------- -------- 
  

Вторая часть запроса в основном просто сокращает эту таблицу до необходимого минимума, а затем создает новый вид «идентификатора компании» ( MyNewCompanyID ) как сумму всех заказов, используемых этой компанией. В вашем примере он возвращает следующую таблицу:

  --------- ---------------- 
| contact | MyNewCompanyID |
 --------- ---------------- 
| a       |              6 |
| b       |              6 |
| c       |              6 |
| d       |              6 |
| e       |              9 |
| f       |              9 |
| g       |              9 |
 --------- ---------------- 
  

Что with часть делает

В первой части я определяю что-то вроде временного представления, к которому я могу позже получить доступ как к обычной таблице. Внутри он должен сначала состоять из обычного запроса, объединенного вторым запросом, которому разрешен рекурсивный доступ к самому себе.

Если вы хотите узнать больше об этом виде рекурсии, я рекомендую эти два видео:

Редактировать

Чтобы присвоить уникальный номер каждой компании, вам, вероятно, лучше использовать row_number() как описано здесь:https://www.sqlservertutorial.net/sql-server-window-functions/sql-server-row_number-function /