Альтернатива пересечению в MySQL - PullRequest
59 голосов
/ 12 апреля 2010

Мне нужно реализовать следующий запрос в MySQL.

(select * from emovis_reporting where (id=3 and cut_name= '全プロセス' and cut_name='恐慌') ) 
intersect
( select * from emovis_reporting where (id=3) and ( cut_name='全プロセス' or cut_name='恐慌') )

Я знаю, что пересечение не в MySQL. Поэтому мне нужен другой способ. Пожалуйста, ведите меня.

Ответы [ 8 ]

54 голосов
/ 08 июля 2010

Microsoft SQL Server INTERSECT "возвращает любые различные значения, которые возвращаются как запросом слева, так и справа от операнда INTERSECT" Это отличается от стандартного INNER JOIN или WHERE EXISTS запрос.

SQL Server

CREATE TABLE table_a (
    id INT PRIMARY KEY,
    value VARCHAR(255)
);

CREATE TABLE table_b (
    id INT PRIMARY KEY,
    value VARCHAR(255)
);

INSERT INTO table_a VALUES (1, 'A'), (2, 'B'), (3, 'B');
INSERT INTO table_b VALUES (1, 'B');

SELECT value FROM table_a
INTERSECT
SELECT value FROM table_b

value
-----
B

(1 rows affected)

MySQL

CREATE TABLE `table_a` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `value` varchar(255),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE `table_b` LIKE `table_a`;

INSERT INTO table_a VALUES (1, 'A'), (2, 'B'), (3, 'B');
INSERT INTO table_b VALUES (1, 'B');

SELECT value FROM table_a
INNER JOIN table_b
USING (value);

+-------+
| value |
+-------+
| B     |
| B     |
+-------+
2 rows in set (0.00 sec)

SELECT value FROM table_a
WHERE (value) IN
(SELECT value FROM table_b);

+-------+
| value |
+-------+
| B     |
| B     |
+-------+

В этом конкретном вопросе задействован столбец id, поэтому повторяющиеся значения возвращаться не будут, но для полноты, вот альтернатива MySQL, использующая INNER JOIN и DISTINCT:

SELECT DISTINCT value FROM table_a
INNER JOIN table_b
USING (value);

+-------+
| value |
+-------+
| B     |
+-------+

И еще один пример использования WHERE ... IN и DISTINCT:

SELECT DISTINCT value FROM table_a
WHERE (value) IN
(SELECT value FROM table_b);

+-------+
| value |
+-------+
| B     |
+-------+
37 голосов
/ 10 июня 2013

Существует более эффективный способ создания пересечения, используя UNION ALL и GROUP BY. Производительность в два раза лучше по моим тестам на больших наборах данных.

Пример:

SELECT t1.value from (
  (SELECT DISTINCT value FROM table_a)
  UNION ALL 
  (SELECT DISTINCT value FROM table_b)
) AS t1 GROUP BY value HAVING count(*) >= 2;

Это более эффективно, потому что с решением INNER JOIN MySQL будет искать результаты первого запроса, а затем для каждой строки искать результат во втором запросе. С помощью решения UNION ALL-GROUP BY оно будет запрашивать результаты первого запроса, результаты второго запроса, а затем группировать результаты все вместе сразу.

15 голосов
/ 12 апреля 2010

Ваш запрос всегда будет возвращать пустой набор записей, поскольку cut_name= '全プロセス' and cut_name='恐慌' никогда не будет оцениваться в true.

В общем случае INTERSECT в MySQL следует эмулировать следующим образом:

SELECT  *
FROM    mytable m
WHERE   EXISTS
        (
        SELECT  NULL
        FROM    othertable o
        WHERE   (o.col1 = m.col1 OR (m.col1 IS NULL AND o.col1 IS NULL))
                AND (o.col2 = m.col2 OR (m.col2 IS NULL AND o.col2 IS NULL))
                AND (o.col3 = m.col3 OR (m.col3 IS NULL AND o.col3 IS NULL))
        )

Если в обеих ваших таблицах есть столбцы, помеченные как NOT NULL, вы можете пропустить части IS NULL и переписать запрос с более эффективным IN:

SELECT  *
FROM    mytable m
WHERE   (col1, col2, col3) IN
        (
        SELECT  col1, col2, col3
        FROM    othertable o
        )
4 голосов
/ 24 октября 2016

Я только что проверил это в MySQL 5.7 и очень удивлен, что никто не предложил простой ответ: ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ

Когда таблицы или (выберите результат) имеют ИДЕНТИЧНЫЕ столбцы, вы можете использовать ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ для поиска пересечения:

enter image description here

Например:

table1

id, name, jobid

'1', 'Джон', '1'

'2', 'Джек', '3'

«3», «Адам», «2»

'4', 'Bill', '6'

table2

id, name, jobid

'1', 'Джон', '1'

'2', 'Джек', '3'

'3', 'Адам', '2'

«4», «Билл», «5»

'5', 'Макс', '6'

А вот и запрос:

SELECT * FROM table1 NATURAL JOIN table2;

Результат запроса: id, name, jobid

'1', 'Джон', '1'

'2', 'Джек', '3'

'3', 'Адам', '2'

1 голос
/ 09 июня 2012

Для полноты приведем еще один способ эмуляции INTERSECT. Обратите внимание, что форма IN (SELECT ...), предложенная в других ответах, как правило, более эффективна.

Обычно для таблицы с именем mytable с первичным ключом с именем id:

SELECT id
FROM mytable AS a
INNER JOIN mytable AS b ON a.id = b.id
WHERE
(a.col1 = "someval")
AND
(b.col1 = "someotherval")

(Обратите внимание, что если вы используете SELECT * с этим запросом, вы получите вдвое больше столбцов, чем определено в mytable, это потому, что INNER JOIN создает декартово произведение )

INNER JOIN здесь генерирует каждую перестановку пар строк из вашей таблицы. Это означает, что каждая комбинация строк генерируется в каждом возможном порядке. Предложение WHERE затем фильтрует сторону пары a, а затем сторону b. В результате возвращаются только строки, удовлетворяющие обоим условиям, так же, как и два пересечения.

0 голосов
/ 16 июня 2015

Разбейте вашу проблему на 2 утверждения: во-первых, вы хотите выбрать все, если

(id=3 and cut_name= '全プロセス' and cut_name='恐慌')

верно. Во-вторых, вы хотите выбрать все, если

(id=3) and ( cut_name='全プロセス' or cut_name='恐慌')

верно. Таким образом, мы присоединимся к ИЛИ, потому что мы хотим выбрать все, если кто-либо из них верен.

select * from emovis_reporting
    where (id=3 and cut_name= '全プロセス' and cut_name='恐慌') OR
        ( (id=3) and ( cut_name='全プロセス' or cut_name='恐慌') )
0 голосов
/ 27 мая 2015
SELECT
  campo1,
  campo2,
  campo3,
  campo4
FROM tabela1
WHERE CONCAT(campo1,campo2,campo3,IF(campo4 IS NULL,'',campo4))
NOT IN
(SELECT CONCAT(campo1,campo2,campo3,IF(campo4 IS NULL,'',campo4))
FROM tabela2);
0 голосов
/ 12 апреля 2010

AFAIR, MySQL реализует INTERSECT через INNER JOIN .

...