2015-12-13 2 views
2

Добрый вечер,Как «обратить вспять» простую таблицу отношений двух столбцов

Я новичок в SQL.

Я пытался «перевернуть» простую таблицу отношений двух столбцов.

Вот пример, чтобы объяснить:

ТАБЛИЦА KnowEachOther

¦  id_human1 ¦  id_human2  ¦ 
¦  1  ¦   10   ¦ 
¦  1  ¦   11   ¦ 
¦  2  ¦   12   ¦ 
¦  2  ¦   13   ¦ 

Так вот у меня есть таблица людей, которые знают друг друга. Каждая строка означает humanX и humanY знают друг друга. Теперь я хочу получить таблицу людей, которые не знают друг друга (при условии, что в этой таблице есть все люди). Это дало бы это:

Таблица DontKnowEachOther

¦  id_human1 ¦  id_human2  ¦ 
¦  1  ¦   2   ¦ 
¦  1  ¦   12   ¦ 
¦  1  ¦   13   ¦ 
¦  2  ¦   10   ¦ 
¦  2  ¦   11   ¦ 
¦  11  ¦   10   ¦ 
¦  12  ¦   10   ¦ 
¦  13  ¦   10   ¦ 
¦  11  ¦   12   ¦ 
¦  11  ¦   13   ¦ 
¦  12  ¦   13   ¦ 

Любые намеки о том, как сделать это будет оценено. Что еще более ценится, так это то, как вы подходите к такой нетривиальной задаче. Попытка поправиться здесь;).

Большое спасибо

+0

Перекрестное соединение и внешнее соединение, где null – Strawberry

+0

У вас есть другая таблица «Человек», в которой перечислены все их идентификаторы? – Don

ответ

1

Во-первых, давайте начнем с чем-то вы забыли. (Если вы действительно не хотите, чтобы это, можно вычислить из таблиц, которые Вы дали, но это, конечно, лучше иметь его.)

CREATE TABLE Humans 
    (
    id int PRIMARY KEY 
); 

INSERT INTO Humans (id) VALUES (1),(2),(11),(12),(13); 

Тогда, вот ваш стол и данные:

CREATE TABLE KnowEachOther 
    (
    id1 INT, 
    id2 INT, 
    PRIMARY KEY (id1, id2) 
); 

INSERT INTO KnowEachOther (id1, id2) VALUES 
(1,10), 
(1,11), 
(2,12), 
(2,13); 

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

CREATE VIEW AllPossibleRelationships AS SELECT 
    h1.id AS id1, 
    h2.id AS id2 
FROM Humans AS h1 
    CROSS JOIN Humans AS h2 
WHERE h1.id <> h2.id; 

И тогда мы можем создать представление, которое удаляет из «всех возможных отношений» те строки, для которых существуют Relat ionships.(См WHERE k.id1 IS NULL)

CREATE VIEW DontKnowEachOther AS SELECT 
    a.id1 AS id1, 
    a.id2 AS id2 
FROM AllPossibleRelationships AS a 
    LEFT JOIN KnowEachOther AS k 
     ON (a.id1 = k.id1 AND a.id2 = k.id2) OR 
      (a.id1 = k.id2 AND a.id2 = k.id1) 
WHERE k.id1 IS NULL 
ORDER BY a.id1; 

Таким образом, выполнение SELECT * FROM DontKnowEachOther; дает следующее:

id1 id2 
1 2 
1 12 
1 13 
2 1 
2 11 
11 2 
11 12 
11 13 
12 1 
12 11 
12 13 
13 1 
13 11 
13 12 

Примечание: есть немного двусмысленности относительно содержимого вашего KnowEachOther таблицы и что это значит «знать друг друга". «Знание друг друга» - это неориентированная связь, означающая, что если A знает B, тогда B также знает A. В свете этого ваша таблица «знаю друг друга» может рассматриваться как неявно содержащая больше строк; например, поскольку у вас есть строка для (1, 10), то подразумевается строка (10, 1). Мои результаты учитывают эти подразумеваемые строки и включают в себя все подразумеваемые и не подразумеваемые строки в результатах.

Фильтрация строк, которые могут подразумеваться, остается в качестве упражнения для читателя.

+1

Спасибо. Это то, что @Strawberry означает «Cross join и external join, где null», я работал над этим. Я не хотел создавать человеческую базу данных, но я могу просто просмотреть ее и использовать ее после. Не знал о VIEW (новый для sql), очень удобный. – shrimpdrake

0

Вы должны сначала получить все возможные KnowEachOther-пары, а затем выбрать те из них, которые не являются в KnowEachOther.

Поскольку вы не указали таблицу, в которой перечислены все люди, вам необходимо использовать union для объединения id_human1 и id_human1 с KnowEachOther. Присоединив результат к себе, вы получите все возможные пары.

Запрос будет проще, если у вас есть отдельная таблица humans.

select * 
from 
    (
    select id_human1 as id_human1 
    from KnowEachOther 
    union 
    select id_human2 
    from KnowEachOther 
) humans1 
    join 
    (
    select id_human1 as id_human2 
    from KnowEachOther 
    union 
    select id_human2 
    from KnowEachOther 
) humans2 on humans1.id_human1!=humans2.id_human2 
where not exists (
select * 
from KnowEachOther k 
where k.id_human1=humans1.id_human1 and k.id_human2=humans2.id_human2 
) 
order by humans1.id_human1, humans2.id_human2 
+0

Это кажется более подробным решением, чем необходимо – Strawberry

+0

Действительно, он слишком многословный и дает неправильные ассоциации. Вот результат: id_human1 | ​​id_human2- 1 | 2- 1 | 12- 1 | 13- 2 | 1- 2 | 10- 2 | 11- 10 | 1- 10 | 2- 10 | 11- 10 | 12- 10 | 13- 11 | 1- 11 | 2- 11 | 10- 11 | 12- 11 | 13- | 12 | 1- | 12 | 2 | 12 | 10- | 12 | 11- | 12 | 13- | 13 | 1- | 13 | 2- | 13 | 10- | 13 | 11- | 13 | 12- – shrimpdrake

+0

Принимал функции KnowEachPother в одном направлении. Если намерение заключается в том, что он работает в обоих направлениях, вы можете изменить предложение where, чтобы сказать: 'где (k.id_human1 = human1.id_human1 и k.id_human2 = human2.id_human2) или (k.id_human1 = people2.id_human2 и k.id_human2 = people1.id_human1) ' – slaakso

0

Без отдельного человека таблицы, создать КТР с помощью WITH упростить логику конечного SQL.

WITH HumanIDs AS 
     (SELECT 
      [HumanID] 
     FROM 
      (
       SELECT id_human1 AS [HumanID] FROM @KnowEachOther 
       UNION 
       SELECT id_human2 AS [HumanID] FROM @KnowEachOther 
       WHERE id_human2 NOT IN (SELECT id_human1 FROM @KnowEachOther) 
      ) x) 

Затем присоединитесь к HumanIDs вместе, создав все возможные пары. Затем удалите все пары, которые существуют при использовании WHERE NOT EXISTS

SELECT DISTINCT 
    h1.HumanID, h2.HumanID 
FROM 
    HumanIDs AS h1 
    INNER JOIN HumanIDs AS h2 ON h1.HumanID <> h2.HumanID 
WHERE NOT EXISTS 
    (
     SELECT 
      * 
     FROM 
      KnowEachOther AS ke 
     WHERE 
      ke.id_human1 = h1.HumanID 
      AND ke.id_human2 = h2.HumanID 
    ) 
ORDER BY 
    h1.HumanID, h2.HumanID 
Смежные вопросы