Альтернатива пересечению в 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);