Альтернатива пересечению в MySQL

Мне нужно реализовать следующий запрос в 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. Поэтому мне нужен другой способ. Пожалуйста, ведите меня.

11 ответов

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     |
+-------+

Существует более эффективный способ создания пересечения, используя 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 он будет запрашивать результаты первого запроса, результаты второго запроса, а затем группировать результаты все вместе сразу.

Ваш запрос всегда будет возвращать пустой набор записей, так как 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
        )

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

Когда таблицы или (выберите результат) имеют столбцы IDENTICAL, вы можете использовать NATURAL JOIN как способ найти пересечение:

Например:

таблица1:

id, name, jobid

"1", "Джон", "1"

"2", "Джек", "3"

"3", "Адам", "2"

"4", "Билл", "6"

таблица2:

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"

Для полноты вот еще один метод для подражания 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 боковая сторона. В результате возвращаются только те строки, которые удовлетворяют обоим условиям, так же, как и два пересечения.

Начиная с MySQL 8.0.31 INTERSECT изначально поддерживается.

Пункт ПЕРЕСЕЧЕНИЯ :

ВЫБРАТЬ... ПЕРЕСЕЧАТЬ [ВСЕ | РАЗЛИЧНЫЕ] ВЫБРАТЬ ... [ПЕРЕСЕЧАТЬ [ВСЕ | РАЗЛИЧНЫЕ] ВЫБЕРИТЕ ...]

INTERSECT ограничивает результат нескольких операторов SELECT теми строками, которые являются общими для всех.

Образец:

       SELECT 1 AS col
 INTERSECT 
 SELECT 1 AS col;

 -- output
 1

Эмуляция INTERSECT через INNER JOIN или IN() может работать только с несколькими наборами соединений 2-4. Если вы попытаетесь найти взаимодействие между несколькими наборами, вам необходимо использовать несколько INNER JOIN. Но множественные INNER JOIN в MySQL - абсолютно нестабильное решение, которое может привести к запутыванию исполнителя плана запроса, который в нашем случае просто зависает на несколько дней со 100% загрузкой процессора.

Вам необходимо обновить MySQL до версии 8.0.31. Он содержит оператор INTERSECT.

Разбейте свою проблему на 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='恐慌') )

Я использую IN для создания пересечения

Это пример использования:

      SELECT * FROM `test_oc_product` 
WHERE product_id IN ( SELECT product_id FROM test_oc_product_option WHERE option_id = '21' AND value = 'Red'  )
AND product_id IN ( SELECT product_id FROM test_oc_product_attribute WHERE attribute_id = '10' )

А вот дамп

      CREATE TABLE `test_oc_product` (
  `product_id` int(11) NOT NULL AUTO_INCREMENT,
  `model` varchar(64) NOT NULL,
  `sku` varchar(64) NOT NULL,
   PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `test_oc_product` (`product_id`, `model`, `sku`) VALUES
(1, 'A1', 'A1'),
(2, 'A2', 'A2'),
(3, 'A3', 'A3'),
(4, 'AA', 'AA'),
(5, 'A5', 'A5'),
(6, 'A6', 'A6'),
(7, 'A7', 'A7'),
(8, 'A8', 'A8');

CREATE TABLE `test_oc_product_attribute` (
  `product_id` int(11) NOT NULL,
  `attribute_id` int(11) NOT NULL,
  `text` text NOT NULL,
  PRIMARY KEY (`product_id`, `attribute_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `test_oc_product_attribute` (`product_id`, `attribute_id`, `text`) VALUES 
('1', '10', 'Attribute Value 1'), 
('2', '11', 'Attribute Value 2');


CREATE TABLE `test_oc_product_option` (
  `product_option_id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `option_id` int(11) NOT NULL,
  `value` text NOT NULL,
   PRIMARY KEY (`product_option_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `test_oc_product_option` (`product_option_id`, `product_id`, `option_id`, `value`) VALUES 
(NULL, '1', '21', 'Red'), 
(NULL, '2', '21', 'Red'),
(NULL, '3', '21', 'Blue'),
(NULL, '4', '10', 'S');
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);

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

Другие вопросы по тегам