SQLinfo.ru - Все о MySQL

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 05.05.2008 16:56:45

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Подзапросы

Проскочило про некорректную работу подзапросов

http://sqlinfo.ru/forum/viewtopic.php?id=544

Не нашел подробностей по этой баге.
Возможно ли оценить вероятность ее проявления в будущем, если на тестовых данных она не проявляется?

Неактивен

 

#2 05.05.2008 17:08:22

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

Это набор недоработок, связанных с неоптимальной работой оптимизатора MySQL. Например часто константные подзапросы в условии WHERE вычисляются для каждой записи основной таблицы. Если для Вашего запроса все нормально, то, вероятно так и будет. Информацию о выполнении запроса может также дать EXPLAIN.

Исправить обещают в 6.0 http://dev.mysql.com/doc/refman/6.0/en/ … shell.html

Неактивен

 

#3 05.05.2008 17:12:35

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

Спасибо.

Explain пишет все Ок.

Неактивен

 

#4 06.05.2008 11:16:32

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

Есть простой способ проверить сколько раз выполняется подзапрос - добавить в него SLEEP(1), например

SELECT * FROM t WHERE id = (SELECT MAX(id)+SLEEP(1) FROM t);


На каждое выполнение подзапроса будет задержка по 1 секунде

Неактивен

 

#5 06.05.2008 14:19:25

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

Хорошая идея.
Проверил - один раз.

Неактивен

 

#6 07.05.2008 14:50:10

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

Все таки попал на один запрос.

В максимально упрощенном виде

SELECT distinct t1.id_t2
FROM t1
WHERE t1.id_t2 NOT IN
         (SELECT t2.id_t2 FROM t2, t1
          WHERE t1.id_t1 = $param AND
                t2.id_pr = t1.id_t2 AND
                t2.id_pr <> $param) ;

mysql> use test;
Database changed
mysql> create table t1 (id_t1 int, id_t2 int);
Query OK, 0 rows affected (0.09 sec)

mysql> insert into t1 values (1,1), (1,2), (1,3), (1,4), (2,1), (2,2), (2,3);
Query OK, 7 rows affected (0.03 sec)
Records: 7  Duplicates: 0  Warnings: 0

mysql> create table t2 (id_t2 int, id_pr int);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into t2 values (1,0), (2,1), (3,1), (4,2);
Query OK, 4 rows affected (0.03 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from t1;
+-------+-------+
| id_t1 | id_t2 |
+-------+-------+
|     1 |     1 |
|     1 |     2 |
|     1 |     3 |
|     1 |     4 |
|     2 |     1 |
|     2 |     2 |
|     2 |     3 |
+-------+-------+
7 rows in set (0.02 sec)

mysql> select * from t2;
+-------+-------+
| id_t2 | id_pr |
+-------+-------+
|     1 |     0 |
|     2 |     1 |
|     3 |     1 |
|     4 |     2 |
+-------+-------+
4 rows in set (0.02 sec)

mysql> SELECT distinct t1.id_t2
    -> FROM t1
    -> WHERE t1.id_t2 NOT IN
    ->          (SELECT t2.id_t2 FROM t2, t1
    ->           WHERE t1.id_t1 = 1 AND
    ->                 t2.id_pr = t1.id_t2 AND
    ->                 t2.id_pr <> 1) ;
+-------+
| id_t2 |
+-------+
|     1 |
|     2 |
|     3 |
+-------+
3 rows in set (0.02 sec)

mysql> SELECT distinct t1.id_t2+sleep(1)
    -> FROM t1
    -> WHERE t1.id_t2 NOT IN
    ->          (SELECT t2.id_t2 FROM t2, t1
    ->           WHERE t1.id_t1 = 1 AND
    ->                 t2.id_pr = t1.id_t2 AND
    ->                 t2.id_pr <> 1) ;
+-------------------+
| t1.id_t2+sleep(1) |
+-------------------+
|                 1 |
|                 2 |
|                 3 |
+-------------------+
3 rows in set (6.00 sec)

mysql>

Не хватает мозгов сделать без подзапроса

Неактивен

 

#7 07.05.2008 15:00:44

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

Не совсем точно сформулировал исходный запрос. Правильно выглядит так

SELECT distinct t1.id_t2
FROM t1
WHERE t1.id_t2 NOT IN
         (SELECT t2.id_t2 FROM t2, t1
          WHERE t1.id_t1 = $param1 AND
                t2.id_pr = t1.id_t2 AND
                t2.id_pr <> $param2) ;

Неактивен

 

#8 07.05.2008 16:20:02

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5829

Re: Подзапросы

EugeneTM написал:

Все таки попал на один запрос.

В максимально упрощенном виде

SELECT distinct t1.id_t2
FROM t1
WHERE t1.id_t2 NOT IN
         (SELECT t2.id_t2 FROM t2, t1
          WHERE t1.id_t1 = $param AND
                t2.id_pr = t1.id_t2 AND
                t2.id_pr <> $param) ;


Не хватает мозгов сделать без подзапроса


SELECT distinct t11.id_t2
     FROM t1 as t11 left join (t2, t1) ON t11.id_t2<>t2.id_t2
               WHERE t1.id_t1 = 1 AND
                     t2.id_pr = t1.id_t2 AND
                     t2.id_pr <> 1;
 

Но в Вашем примере подзапрос работает совершенно корректно.

Неактивен

 

#9 07.05.2008 16:25:04

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

Предлагаю использовать новые bbcode-теги {syntax=mysql} код на mysql {/syntax} (фигурные скобки заменить на квадратные)

Неактивен

 

#10 07.05.2008 16:25:46

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

EugeneTM написал:

Не совсем точно сформулировал исходный запрос. Правильно выглядит так

SELECT distinct t1.id_t2
FROM t1
WHERE t1.id_t2 NOT IN
         (SELECT t2.id_t2 FROM t2, t1
          WHERE t1.id_t1 = $param1 AND
                t2.id_pr = t1.id_t2 AND
                t2.id_pr <> $param2) ;

В вашем случае подзапрос зависимый от основного запроса, а не константный, поэтому должен выполняться много раз

Неактивен

 

#11 07.05.2008 17:24:00

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

С точки зрения возвращаемого результата запрос абсолютно корректен.

Но с точки зрения эффективности порочен.
Разложим его по полочкам

Выполним только сам подзапрос

mysql> SELECT t2.id_t2 FROM t2, t1
    ->           WHERE t1.id_t1 = 1 AND
    ->                 t2.id_pr = t1.id_t2 AND
    ->                 t2.id_pr <> 1;
+-------+
| id_t2 |
+-------+
|     4 |
+-------+
1 row in set (0.03 sec)

mysql>

Он возвращает одну строку и выполняется за 0.03 сек

Слегка видоизменим его. Добавим задержку в 1 сек к каждой возвращаемой строке.

mysql> SELECT t2.id_t2+sleep(1) FROM t2, t1
    ->            WHERE t1.id_t1 = 1 AND
    ->                  t2.id_pr = t1.id_t2 AND
    ->                  t2.id_pr <> 1;
+-------------------+
| t2.id_t2+sleep(1) |
+-------------------+
|                 4 |
+-------------------+
1 row in set (1.02 sec)

mysql>

Получили как и следовало ожидать чуть больше 1 секунды время выполнения.

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

mysql> SELECT distinct t1.id_t2
    -> FROM t1
    -> WHERE t1.id_t2 NOT IN
    ->          (SELECT t2.id_t2 + sleep(1)  FROM t2, t1
    ->           WHERE t1.id_t1 =1 AND
    ->                 t2.id_pr = t1.id_t2 AND
    ->                 t2.id_pr <> 1) ;
+-------+
| id_t2 |
+-------+
|     1 |
|     2 |
|     3 |
+-------+
3 rows in set (27.03 sec)

mysql>

Караул. Время выполнения 27 сек.
Посмотрим откуда они растут.
Выполним основной запрос прибив дистинкт и ограничения накладываемые подзапросом

mysql> SELECT t1.id_t2
    -> FROM t1
    -> ;
+-------+
| id_t2 |
+-------+
|     1 |
|     2 |
|     3 |
|     4 |
|     1 |
|     2 |
|     3 |
+-------+
7 rows in set (0.02 sec)

mysql>

Итого семь записей.
Как в ссылке в начале топика звучало, можно было бы предположить - подзапрос вызывается каждый раз для сравнения строки с результатом подзапроса. Но тогда он должен вызываться семь раз и время выполнения должно быть чуть больше семи секунд. Это само по себе неприемлемо. Что будет при работе с реальными данными, при 100 - 300К записей.
Картина же намного хуже. Непонятно почему запрос выполняется 27 сек.

Ну и резюме, данный запрос нужно выполнить без использования подзапроса. А тут я уже голову сломал.

Неактивен

 

#12 07.05.2008 17:31:35

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

MySQL не обязан выполнять ровно 7 раз. Как переписать через JOIN см. ответ #8 в теме

Неактивен

 

#13 07.05.2008 17:41:15

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

Подтормаживаю.
Спасибо.

Неактивен

 

#14 08.05.2008 07:34:30

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

SELECT DISTINCT t11.id_t2
     FROM t1 AS t11 LEFT JOIN (t2, t1) ON t11.id_t2<>t2.id_t2
               WHERE t1.id_t1 = 1 AND
                     t2.id_pr = t1.id_t2 AND
                     t2.id_pr <> 1;

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

mysql> use test;
Database changed
mysql> SELECT distinct t1.id_t2
    -> FROM t1
    -> WHERE t1.id_t2 NOT IN
    ->          (SELECT t2.id_t2 FROM t2, t1
    ->           WHERE t1.id_t1 = 1 AND
    ->                 t2.id_pr = t1.id_t2 AND
    ->                 t2.id_pr <> 2) ;
+-------+
| id_t2 |
+-------+
|     1 |
|     4 |
+-------+
2 rows in set (0.02 sec)


mysql> SELECT DISTINCT t11.id_t2
    ->      FROM t1 AS t11 LEFT JOIN (t2, t1) ON t11.id_t2<>t2.id_t2
    ->                WHERE t1.id_t1 = 1 AND
    ->                      t2.id_pr = t1.id_t2 AND
    ->                      t2.id_pr <> 2;
+-------+
| id_t2 |
+-------+
|     1 |
|     2 |
|     3 |
|     4 |
+-------+
4 rows in set (0.00 sec)

mysql>

Неактивен

 

#15 08.05.2008 09:40:54

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

Попробуйте такой запрос. Если не получится, выложите дамп тестовой таблицы.


SELECT DISTINCT t11.id_t2
     FROM t1 AS t11 LEFT JOIN (t2, t1) ON t11.id_t2=t2.id_t2
               WHERE t1.id_t1 = 1 AND
                     t2.id_pr = t1.id_t2 AND
                     t2.id_pr <> 1
                     AND t2.id_t2 IS NULL;
 

Неактивен

 

#16 08.05.2008 09:51:02

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

rgbeast написал:

Попробуйте такой запрос. Если не получится, выложите дамп тестовой таблицы.


SELECT DISTINCT t11.id_t2
     FROM t1 AS t11 LEFT JOIN (t2, t1) ON t11.id_t2=t2.id_t2
               WHERE t1.id_t1 = 1 AND
                     t2.id_pr = t1.id_t2 AND
                     t2.id_pr <> 1
                     AND t2.id_t2 IS NULL;
 

Пробовал.
Возвращает пустой результат.

mysql> create table t1 (id_t1 int, id_t2 int);
Query OK, 0 rows affected (0.09 sec)

mysql> insert into t1 values (1,1), (1,2), (1,3), (1,4), (2,1), (2,2), (2,3);
Query OK, 7 rows affected (0.03 sec)
Records: 7  Duplicates: 0  Warnings: 0

mysql> create table t2 (id_t2 int, id_pr int);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into t2 values (1,0), (2,1), (3,1), (4,2);
Query OK, 4 rows affected (0.03 sec)
Records: 4  Duplicates: 0  Warnings: 0

Неактивен

 

#17 08.05.2008 10:02:41

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

Попробуйте так:


SELECT DISTINCT t11.id_t2      FROM t1 AS t11 LEFT JOIN t2 ON t11.id_t2=t2.id_t2 AND t2.id_pr<>2 LEFT JOIN t1 ON t2.id_pr=t1.id_t2 AND t1.id_t1=1 WHERE t1.id_t2 IS NULL;
 


Как вариант - выполните сначала подзапрос, а потом его результат вставьте в основной запрос, средствами PHP.

Неактивен

 

#18 08.05.2008 10:13:54

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

rgbeast написал:

Попробуйте так:


SELECT DISTINCT t11.id_t2      FROM t1 AS t11 LEFT JOIN t2 ON t11.id_t2=t2.id_t2 AND t2.id_pr<>2 LEFT JOIN t1 ON t2.id_pr=t1.id_t2 AND t1.id_t1=1 WHERE t1.id_t2 IS NULL;
 


Как вариант - выполните сначала подзапрос, а потом его результат вставьте в основной запрос, средствами PHP.

На этом наборе отрабатывает нормально.
Поиграюсь с более широким набором данных.


PHP не хотелось бы прикручивать сюда. А вот если можно запросу передать результаты выполнения ХП, то возможно оно то что нужно.
Пошел мануал читать.

Неактивен

 

#19 08.05.2008 10:17:14

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

PHP не хотелось бы прикручивать сюда. А вот если можно запросу передать результаты выполнения ХП, то возможно оно то что нужно. Пошел мануал читать.

Хранимую процедуру можно написать для такой цели. А вы уверены, что этот запрос у вас медленный? Преждевременная оптимизация - корень всех зол.

Неактивен

 

#20 08.05.2008 11:54:06

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

rgbeast написал:

Попробуйте так:


SELECT DISTINCT t11.id_t2      FROM t1 AS t11 LEFT JOIN t2 ON t11.id_t2=t2.id_t2 AND t2.id_pr<>2 LEFT JOIN t1 ON t2.id_pr=t1.id_t2 AND t1.id_t1=1 WHERE t1.id_t2 IS NULL;
 

Увы.
Тоже не корректен. Вылезло на реальных данных. Смоделировать удалось.

mysql> use test;
Database changed
mysql> drop table if exists t1;
Query OK, 0 rows affected (0.13 sec)

mysql> drop table if exists t2;
Query OK, 0 rows affected (0.03 sec)

mysql> create table t1 (id_t1 int, id_t2 int);
Query OK, 0 rows affected (0.09 sec)

mysql> create table t2 (id_t2 int,id_pr int);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t1 values (1,1), (1,2), (1,3), (1,4), (1,7), (2,1), (2,3), (2,5), (2,6), (2,7), (2,8);
Query OK, 11 rows affected (0.03 sec)
Records: 11  Duplicates: 0  Warnings: 0

mysql> insert into t2 values (1,0), (2,1), (3,1), (4,2), (5,3), (6,4), (7,5), (8,6), (6,2), (6,1), (6,0);
Query OK, 11 rows affected (0.02 sec)
Records: 11  Duplicates: 0  Warnings: 0

mysql> select * from t1;
+-------+-------+
| id_t1 | id_t2 |
+-------+-------+
|     1 |     1 |
|     1 |     2 |
|     1 |     3 |
|     1 |     4 |
|     1 |     7 |
|     2 |     1 |
|     2 |     3 |
|     2 |     5 |
|     2 |     6 |
|     2 |     7 |
|     2 |     8 |
+-------+-------+
11 rows in set (0.00 sec)

mysql> select * from t2;
+-------+-------+
| id_t2 | id_pr |
+-------+-------+
|     1 |     0 |
|     2 |     1 |
|     3 |     1 |
|     4 |     2 |
|     5 |     3 |
|     6 |     4 |
|     7 |     5 |
|     8 |     6 |
|     6 |     2 |
|     6 |     1 |
|     6 |     0 |
+-------+-------+
11 rows in set (0.00 sec)

mysql> SELECT distinct t1.id_t2
    -> FROM t1
    -> WHERE t1.id_t2 NOT IN
    ->          (SELECT t2.id_t2 FROM t2, t1
    ->           WHERE t1.id_t1 = 1 AND
    ->                 t2.id_pr = t1.id_t2 AND
    ->                 t2.id_pr <> 1) ;
+-------+
| id_t2 |
+-------+
|     1 |
|     2 |
|     3 |
|     7 |
|     8 |
+-------+
5 rows in set (0.01 sec)

mysql> SELECT DISTINCT t11.id_t2
    -> FROM t1 AS t11
    -> LEFT JOIN t2 ON t11.id_t2=t2.id_t2 AND
    ->                 t2.id_pr<>1
    -> LEFT JOIN t1 ON t2.id_pr=t1.id_t2 AND
    ->                 t1.id_t1=1
    -> WHERE t1.id_t2 IS NULL;
+-------+
| id_t2 |
+-------+
|     1 |
|     2 |
|     3 |
|     7 |
|     6 |
|     8 |
+-------+
6 rows in set (0.01 sec)

mysql>

Попытаюсь ХП привинтить.

Неактивен

 

#21 08.05.2008 12:21:28

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

Правильно ли я понял.
Если ХП возвращает набор записей, то использовать я его могу только на клиенте. Но не на сервере.

Неактивен

 

#22 08.05.2008 12:42:24

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

На сервере можете использовать с помощью курсоров. Подзапрос можно переписать через GROUP_CONCAT, тогда он будет возвращать строку.

Неактивен

 

#23 11.05.2008 10:58:40

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

Курсоры смущают своей неспешностью.
GROUP_CONCAT тоже страшновато.
Максимальный размер строки в моем случае 21К - UTF8, боюсь возможны ситуации будет больше.

Неактивен

 

#24 11.05.2008 12:53:53

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Подзапросы

Курсоры далеко не всегда медленные. 21к это для varchar, можно text использовать. Все таки Вы уверены, что указанный исходный  запрос тормозит/будет тормозить?

Неактивен

 

#25 11.05.2008 17:37:25

EugeneTM
Гуру
Зарегистрирован: 11.04.2008
Сообщений: 89

Re: Подзапросы

Кажется въезжать начинаю почему некорректно работает запрос с join'ом
Попытаюсь деградировать на самый нижний уровень и тупо разложить все по полочкам


mysql> select * from t1;
+-------+-------+
| id_t1 | id_t2 |
+-------+-------+
|     1 |     1 |
|     1 |     2 |
|     1 |     3 |
|     1 |     4 |
|     1 |     7 |
|     2 |     1 |
|     2 |     3 |
|     2 |     5 |
|     2 |     6 |
|     2 |     7 |
|     2 |     8 |
+-------+-------+
11 rows in set (0.00 sec)

mysql> select * from t2;
+-------+-------+
| id_t2 | id_pr |
+-------+-------+
|     1 |     0 |
|     2 |     1 |
|     3 |     1 |
|     4 |     2 |
|     5 |     3 |
|     6 |     4 |
|     7 |     5 |
|     8 |     6 |
|     6 |     2 |
|     6 |     1 |
|     6 |     0 |
+-------+-------+
11 rows in set (0.00 sec)

mysql> SELECT distinct t1.id_t2
    -> FROM t1
    -> WHERE t1.id_t2 NOT IN
    ->          (SELECT t2.id_t2 FROM t2, t1
    ->           WHERE t1.id_t1 = 1 AND
    ->                 t2.id_pr = t1.id_t2 AND
    ->                 t2.id_pr <> 1) ;
+-------+
| id_t2 |
+-------+
|     1 |
|     2 |
|     3 |
|     7 |
|     8 |
+-------+
5 rows in set (0.06 sec)

 

Теперь пойдем по порядку
Выполним только подзапрос c единственным ограничением. Только записи в которых t2.id_pr = t1.id_t2.


mysql> SELECT * FROM t2, t1
    -> WHERE t2.id_pr = t1.id_t2;
+-------+-------+-------+-------+
| id_t2 | id_pr | id_t1 | id_t2 |
+-------+-------+-------+-------+
|     2 |     1 |     1 |     1 |
|     3 |     1 |     1 |     1 |
|     6 |     1 |     1 |     1 |
|     4 |     2 |     1 |     2 |
|     6 |     2 |     1 |     2 |
|     5 |     3 |     1 |     3 |
|     6 |     4 |     1 |     4 |
|     2 |     1 |     2 |     1 |
|     3 |     1 |     2 |     1 |
|     6 |     1 |     2 |     1 |
|     5 |     3 |     2 |     3 |
|     7 |     5 |     2 |     5 |
|     8 |     6 |     2 |     6 |
+-------+-------+-------+-------+
13 rows in set (0.03 sec)
 

Добавим еще одно условие.  t2.id_pr <> 1


mysql> SELECT * FROM t2, t1
    ->               WHERE t2.id_pr = t1.id_t2 AND
    ->                               t2.id_pr <> 1;
+-------+-------+-------+-------+
| id_t2 | id_pr | id_t1 | id_t2 |
+-------+-------+-------+-------+
|     4 |     2 |     1 |     2 |
|     6 |     2 |     1 |     2 |
|     5 |     3 |     1 |     3 |
|     6 |     4 |     1 |     4 |
|     5 |     3 |     2 |     3 |
|     7 |     5 |     2 |     5 |
|     8 |     6 |     2 |     6 |
+-------+-------+-------+-------+
7 rows in set (0.02 sec)

 

И наконец  t1.id_t1 = 1


mysql> SELECT * FROM t2, t1
    ->               WHERE t2.id_pr = t1.id_t2 AND
    ->                               t2.id_pr <> 1 AND
    ->                               t1.id_t1 = 1 ;
+-------+-------+-------+-------+
| id_t2 | id_pr | id_t1 | id_t2 |
+-------+-------+-------+-------+
|     4 |     2 |     1 |     2 |
|     6 |     2 |     1 |     2 |
|     5 |     3 |     1 |     3 |
|     6 |     4 |     1 |     4 |
+-------+-------+-------+-------+
4 rows in set (0.02 sec)
 

Если выполнить только сам подзапрос, получиться

mysql> SELECT t2.id_t2 FROM t2, t1
    -> WHERE t1.id_t1 = 1 AND
    ->                  t2.id_pr = t1.id_t2 AND
    ->                  t2.id_pr <> 1;
+-------+
| id_t2 |
+-------+
|     4 |
|     6 |
|     5 |
|     6 |
+-------+
4 rows in set (0.05 sec)
 

t1.id_t2 NOT IN к этому набору
+-------+-------+
| id_t1 | id_t2 |
+-------+-------+
|     1 |     1 |
|     1 |     2 |
|     1 |     3 |
|     1 |     7 |
|     2 |     1 |
|     2 |     3 |
|     2 |     7 |
|     2 |     8 |

DISTINCT на результат
+-------+-------+
| id_t1 | id_t2 |
+-------+-------+
|     1 |     1 |
|     1 |     2 |
|     1 |     3 |
|     1 |     7 |
|     2 |     8 |


Теперь поковыряемся в

rgbeast написал:

Попробуйте так:


SELECT DISTINCT t11.id_t2      
FROM t1 AS t11
LEFT JOIN t2 ON t11.id_t2=t2.id_t2 AND t2.id_pr<>1
LEFT JOIN t1 ON t2.id_pr=t1.id_t2 AND t1.id_t1=1
WHERE t1.id_t2 IS NULL;
 

Посмотрим что отдает первый джойн


mysql> SELECT *
    ->      FROM t1 AS t11
    ->      LEFT JOIN t2 ON t11.id_t2=t2.id_t2 AND t2.id_pr<>1;
+-------+-------+-------+-------+
| id_t1 | id_t2 | id_t2 | id_pr |
+-------+-------+-------+-------+
|     1 |     1 |     1 |     0 |
|     1 |     2 |  NULL |  NULL |
|     1 |     3 |  NULL |  NULL |
|     1 |     4 |     4 |     2 |
|     1 |     7 |     7 |     5 |
|     2 |     1 |     1 |     0 |
|     2 |     3 |  NULL |  NULL |
|     2 |     5 |     5 |     3 |
|     2 |     6 |     6 |     4 |
|     2 |     6 |     6 |     2 |
|     2 |     6 |     6 |     0 |
|     2 |     7 |     7 |     5 |
|     2 |     8 |     8 |     6 |
+-------+-------+-------+-------+
13 rows in set (0.05 sec)
 

Слегка переиначим запрос. Выведем все поля и уберем условие WHERE t1.id_t2 IS NULL

mysql> SELECT *
    -> FROM t1 AS t11
    -> LEFT JOIN t2 ON t11.id_t2=t2.id_t2 AND t2.id_pr<>1
    -> LEFT JOIN t1 ON t2.id_pr=t1.id_t2 AND t1.id_t1=1;
+-------+-------+-------+-------+-------+-------+
| id_t1 | id_t2 | id_t2 | id_pr | id_t1 | id_t2 |
+-------+-------+-------+-------+-------+-------+
|     1 |     1 |     1 |     0 |  NULL |  NULL |
|     1 |     2 |  NULL |  NULL |  NULL |  NULL |
|     1 |     3 |  NULL |  NULL |  NULL |  NULL |
|     1 |     4 |     4 |     2 |     1 |     2 |
|     1 |     7 |     7 |     5 |  NULL |  NULL |
|     2 |     1 |     1 |     0 |  NULL |  NULL |
|     2 |     3 |  NULL |  NULL |  NULL |  NULL |
|     2 |     5 |     5 |     3 |     1 |     3 |
|     2 |     6 |     6 |     4 |     1 |     4 |
|     2 |     6 |     6 |     2 |     1 |     2 |
|     2 |     6 |     6 |     0 |  NULL |  NULL |
|     2 |     7 |     7 |     5 |  NULL |  NULL |
|     2 |     8 |     8 |     6 |  NULL |  NULL |
+-------+-------+-------+-------+-------+-------+
13 rows in set (0.02 sec)
 

Запись которая нам не нужна
+-------+-------+-------+-------+-------+-------+
| id_t1 | id_t2 | id_t2 | id_pr | id_t1 | id_t2 |
+-------+-------+-------+-------+-------+-------+
|     2 |     6 |     6 |     0 |  NULL |  NULL |

В подзапросе она была прибита из выдачи условием  t2.id_pr = t1.id_t2 и больше нигде не мешалась.
В варианте с джойном она не проходит условие t2.id_pr=t1.id_t2 и естественно id_t2 получает NULL  и соответственно выдается по WHERE t1.id_t2 IS NULL.

Про как с этим бороться мыслей пока нет.

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson