#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 /