Как сделать запрос элементов с одинаковым набором значений - PullRequest
4 голосов
/ 15 июня 2011

Я ищу эффективный способ запроса элементов с одинаковым набором значений.

У меня есть следующая таблица

C_1  C_2
--------
A    1
A    2
-------
B    1
B    2
B    3
-------
C    1
C    2
-------
D    1
D    2
D    3
-------
E    1
E    2 
-------
F    0
F    2 

Я выберу список элементов ОТC_1, которые имеют точно такой же набор элементов c_2, что и данный элемент.

Для элемента A у меня будет

C
E

Для элемента B у меня будет

D 

как это можно сделать в SQL (Oracle 10g)?


Вот инструкция создания таблицы для целей тестирования

create table t (c_1 varchar2(1), c_2 number);
INSERT into t VALUES('A',    1);
INSERT into t VALUES('A',    2);
INSERT into t VALUES('B',    1);
INSERT into t VALUES('B',    2);
INSERT into t VALUES('B',    3);
INSERT into t VALUES('C',    1);
INSERT into t VALUES('C',    2);
INSERT into t VALUES('D',    1);
INSERT into t VALUES('D',    2);
INSERT into t VALUES('D',    3);
INSERT into t VALUES('E',    1);
INSERT into t VALUES('E',    2); 
INSERT into t VALUES('F',    0);
INSERT into t VALUES('F',    2);

Ответы [ 5 ]

3 голосов
/ 15 июня 2011

Вы можете использовать функцию 10g COLLECT; поскольку вы не хотите видеть значения c_2, вам даже не нужно cast it.

select c_1
from t
where c_1 != 'A'
group by c_1
having collect(c_2) = (select collect(c_2) from t where c_1 = 'A' group by c_1)
/
3 голосов
/ 15 июня 2011
create type t_c_2 as table of number;

select one.c_1, two.c_1
from (select t.c_1, CAST(COLLECT(t.c_2) as t_c_2) coll
      from t
      group by t.c_1) one
    ,(select t.c_1, CAST(COLLECT(t.c_2) as t_c_2) coll
      from t
      group by t.c_1) two
where one.coll = two.coll
  and one.c_1 != two.c_1;

Я никогда не использовал такую ​​конструкцию на производственной площадке, необходима проверка эффективности.

2 голосов
/ 15 июня 2011

То, что вам нужно, может быть достигнуто с делением:

http://en.wikipedia.org/wiki/Relational_algebra#Division_.28.C3.B7.29

Или похожий вопрос:

Есть ли установленное деление в SQL?

К сожалению, деления не так легко выразить с помощью SQL, как с реляционной алгеброй.

1 голос
/ 15 июня 2011
SELECT
  [search].c_1,
  [match].c_1
FROM
  t      AS [search]
INNER JOIN
  t      AS [match]
    ON [search].c_2 = [match].c_2
GROUP BY
  [search].c_1,
  [match].c_1
HAVING
      COUNT(*) = (SELECT COUNT(*) FROM t WHERE c_1 = [search].c_1)
  AND COUNT(*) = (SELECT COUNT(*) FROM t WHERE c_1 = [match].c_1)

Для максимальной производительности вам нужно два индекса.Один на c_1, а другой на c_2.
- Индекс на c_1 делает два SELECT COUNT(*) запроса простыми поисками индекса.
- Индекс на c_2 делает поиск соединения.


Вы можете добавить AND [search].c_1 != [match].c_1, чтобы остановить набор, соответствующий самому себе.

0 голосов
/ 15 июня 2011

примерно так:

SELECT DISTINCT c_1
  FROM (SELECT c_1, c_2, COUNT(*) over(PARTITION BY c_1) cnt FROM t)
 WHERE (SELECT MAX(c_2) FROM t WHERE c_1 = 'A') = cnt
   AND c_1 <> 'A'

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...