Модераторы: skyboy
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Выборка с пересечениями множеств, Выбор из join-таблицы по id разных типов 
:(
    Опции темы
Alx
Дата 28.11.2012, 12:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ajaxy
****


Профиль
Группа: Комодератор
Сообщений: 2903
Регистрация: 26.11.2003
Где: Cutopia

Репутация: нет
Всего: 78



Всем привет!

Второй день пытаюсь сообразить, как написать запрос:

Есть таблица user и join-таблица user_skill. Скиллы бывают разных типов, поэтому на вход имеется несколько множеств id-шников скиллов (наборы уже готовые приходят из PHP), и задача выбрать тех юзеров, которые удовлетворяют хотя бы одному скиллу из всех множеств. Например:

user_skill:
-----
user_id | skill_id
    1      |    1
    2      |    2
    1      |    3
    2      |    4
    1      |    5

То есть у user#1 скиллы 1, 3, 5, это все скиллы разных типов и на вход поступает что-то типа
Код

SELECT * FROM user_skill LEFT JOIN user USING(user_id) WHERE *skill_id* IN(1, 2) AND *skill_id* IN(3, 4) AND *skill_id* IN(5, 6)

Это и должно вернуть user#1. (Но не user#2, так как у него нет ни skill#5, ни skill#6).

Заранее спасибо!

PS - есть вариант, что мне вообще надо как-то по-другому архитектуру спроектировать, так что поправьте, если c этим ошибся.


--------------------
PM MAIL WWW ICQ   Вверх
Arantir
Дата 28.11.2012, 13:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: нет
Всего: 55



Код

 IN(1, 2) AND *skill_id* IN(3, 4) AND *skill_id* IN(5, 6)

IN не только по 2 значения принимает. Теоретически - до бесконечности.

Не совсем понял, что именно вы хотите ввести и что на основе этого получить.
Если вы имеете список значений и надо выбрать строку с соответствием хотя бы одному значению, то измените свой запрос, например, так:
Код

$values = array(1, 3, 5, 10, 20, 56, 100, 1000); // Это может быть так же массив, полученный из какого-то другого запроса в базу
$query = "SELECT * FROM user_skill LEFT JOIN user USING(user_id) WHERE *skill_id* IN(".implode(', '$values).")";


Это сообщение отредактировал(а) Arantir - 28.11.2012, 13:22


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
Alx
Дата 28.11.2012, 13:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ajaxy
****


Профиль
Группа: Комодератор
Сообщений: 2903
Регистрация: 26.11.2003
Где: Cutopia

Репутация: нет
Всего: 78



Знаю, что IN не только по два значения принимает, это для примера.

Есть группы ID-шников скиллов (допустим три группы) [1, 2], [3, 4] и [5, 6].

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

Пример таблицы см. выше.


--------------------
PM MAIL WWW ICQ   Вверх
Arantir
Дата 28.11.2012, 13:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Рыбак без удочки
**


Профиль
Группа: Участник
Сообщений: 960
Регистрация: 18.11.2012

Репутация: нет
Всего: 55



Цитата(Alx @  28.11.2012,  12:15 Найти цитируемый пост)
Нужно выбрать пользователей, у которых есть хотя бы по одному скиллу из каждой группы.

А, ну теперь понятнее =)

Тогда логически вроде бы правильно. Если хоть для одного IN ни одного скила не попадется, то будет FALSE.
Но, по-моему, LEFT JOIN вытянет в результаты и неподходящие поля... 
Точнее не поля, а сточки с NULL-ками. Обрабатывать потом неудобно.

Это сообщение отредактировал(а) Arantir - 28.11.2012, 13:35


--------------------
interface Жопа {
    // ATTENTION: has to be implemented by every class of the project for proper project work
}
PM   Вверх
igorold
Дата 28.11.2012, 15:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 557
Регистрация: 22.12.2005
Где: Россия->Урал-& gt;Миасс

Репутация: 5
Всего: 17



Alx, по вашему запросу вы ничего не получите.
Вы хотите выбрать записи из одной таблицы, у которой поле принимает одно значение, а вы хотите, чтобы оно одновременно 

Код

WHERE *skill_id* IN(1, 2) AND *skill_id* IN(3, 4) AND *skill_id* IN(5, 6)


Вам надо запрос строить наоборот - от таблицы user и к ней left join-ом присоединять user_skill - столько раз, сколько у вас есть групп условий, группировать по индексу user-а и выбирать те записи, в которых по всем join-ам индекс присоединенной записи not null


--------------------
... у семи нянек 14 сисек ...  
Putin here, Putin there, Putin almost everywhere!
PM MAIL   Вверх
Akina
Дата 28.11.2012, 15:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 106
Всего: 454



Напрашивается существование таблицы (скилл - группа скиллов). Когда она появится, всё станет достаточно простым делом:

Код

select ИД_перса
from таблицы
where (скиллы in (интересующие значения)) or  (группы_скиллов in (интересующие значения)) 
group by ИД_перса
having count(distinct группа_скиллов) = требуемое_количество_групп



--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
igorold
Дата 28.11.2012, 16:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 557
Регистрация: 22.12.2005
Где: Россия->Урал-& gt;Миасс

Репутация: 5
Всего: 17



А если эти "наборы скилов" динамические и каждый раз разные?
ТС неплохо бы уточнить исходные данные.


--------------------
... у семи нянек 14 сисек ...  
Putin here, Putin there, Putin almost everywhere!
PM MAIL   Вверх
Akina
Дата 28.11.2012, 16:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 106
Всего: 454



Цитата(igorold @  28.11.2012,  17:01 Найти цитируемый пост)
А если эти "наборы скилов" динамические и каждый раз разные?

Тогда это будет сеансовая темп-таблица...


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Alx
Дата 28.11.2012, 18:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ajaxy
****


Профиль
Группа: Комодератор
Сообщений: 2903
Регистрация: 26.11.2003
Где: Cutopia

Репутация: нет
Всего: 78



Akina, спасибо, но не очень понял твой пример, можешь объяснить подробнее?

Скилы пользователь выбирает галочками из нескольких наборов чекбоксов, например, отдельно цвет кожи: светлый, желтый; отдельно цвет глаз: серый, карий; отдельно страны: Россия, Америка, Франция.

Причем таблица skill (грубо говоря) имеет вид

skill_id | type | name
     1    | skin | светлый
     2    | skin | желтый
     3    | skin | черный
     4    | eyes | серый
     5    | eyes | голубой
     6    | eyes | карий
     7    | loc   | Россия
... и т.д.

+ m2m join-таблица пользователь-скилл.


--------------------
PM MAIL WWW ICQ   Вверх
Alx
Дата 6.12.2012, 16:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ajaxy
****


Профиль
Группа: Комодератор
Сообщений: 2903
Регистрация: 26.11.2003
Где: Cutopia

Репутация: нет
Всего: 78



Получилось сделать как советовал igorold:

Код

SELECT * FROM `user` t
    LEFT JOIN user_skill us1 ON us1.user_id = t.user_id
    LEFT JOIN user_skill us2 ON us2.user_id = t.user_id
WHERE
    us1.skill_id IN (1, 2, 3) AND
    us2.skill_id IN (4, 5, 6)
GROUP BY t.user_id


Правда не понадобилась часть
Цитата(igorold @  28.11.2012,  15:11 Найти цитируемый пост)
и выбирать те записи, в которых по всем join-ам индекс присоединенной записи not null 



Насколько этот запрос оптимален по скорости?

Добавлено через 14 минут и 48 секунд
Еще один вариант получилось использовать:

Код

SELECT * FROM `user` t
    LEFT JOIN user_skill us1 ON us1.user_id = t.user_id AND us1.skill_id IN (1, 2, 3)
    LEFT JOIN user_skill us2 ON us2.user_id = t.user_id AND us2.skill_id IN (4, 5, 6)
GROUP BY t.user_id
HAVING us1.user_id > 0 AND us2.user_id > 0 


Какой из них предпочтительнее? Я так думаю второй медленнее, лучше сразу не выбирать лишние записи. Кстати почему то
Код

HAVING us1.user_id IS NOT NULL AND us2.user_id IS NOT NULL

не работало у меня (может что не так написал?), пришлось заменить на > 0

Это сообщение отредактировал(а) Alx - 6.12.2012, 16:10


--------------------
PM MAIL WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | MySQL | Следующая тема »


 




[ Время генерации скрипта: 0.0678 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.