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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Выборка случайных строк, две связанные таблицы, связь 1н к многим 
:(
    Опции темы
ST_Falcon
Дата 16.10.2007, 12:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 330
Регистрация: 14.11.2003
Где: Львов

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



перелопатил поиск в поисках ответа на свой вопрос... но по моему все не то.

у меня есть две таблицы categories и images. 
в одной список категорий. в другой список изображений. связь по id категории. то есть каждое изображение принадлежит  одной из категорий.

задача:
вывести по одному случайному изображению из каждой категории.

как то можно такое организовать одним запросом?
PM MAIL ICQ   Вверх
Anark1
Дата 16.10.2007, 14:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 622
Регистрация: 15.12.2006
Где: RF -> Moscow

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



http://liferay.livejournal.com/7486.html
Стоит немножко изменить, приведенный запрос.


--------------------
Enjoy yourself, still you can...;)

user posted image

user posted image
PM MAIL ICQ   Вверх
SelenIT
Дата 16.10.2007, 17:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 6
Всего: 401



Да просто сгруппировать по id категории. Они и так будут достаточно случайными...


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
ST_Falcon
Дата 16.10.2007, 21:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 330
Регистрация: 14.11.2003
Где: Львов

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



SelenIT, 
Цитата

Да просто сгруппировать по id категории. Они и так будут достаточно случайными... 

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


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

зы. пока сделал несколько запросов которые выбирают одну случайную запись для каждой категории.
PM MAIL ICQ   Вверх
sTa1kEr
Дата 17.10.2007, 07:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


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

Репутация: 7
Всего: 146



Цитата(SelenIT @  16.10.2007,  17:50 Найти цитируемый пост)
Да просто сгруппировать по id категории. Они и так будут достаточно случайными... 

Но при этом каждый раз одинаковыми.

ST_Falcon, В принципе можно вытащить строки из рандомно отсортированной таблицы images с группировкой по id категории, как и предложил SelenIT, но, имхо, это плохой стиль (поле id картинки будет без группировки и без вычисляемой функции)...

Можно попробовать написать хранимку, которая будет проходить циклом по категориям и добавлять во временную таблицу случайное id каждой картинки.

Ну и еще вариант. Добавить для таблицы картинок некое поле order, которое будет пронумеровать строки с сортировкой по id категории при каждом изменении таблицы images (или каждый раз пронумеровать при помощи переменной). Тогда можно будет одним запросом с группировкой выбрать случайные картинки по алгоритму:
   FLOOR(MIN(`order`) + RAND() * (MAX(`order`) - MIN(`ORDER`))) 
PM MAIL   Вверх
ST_Falcon
Дата 17.10.2007, 19:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 330
Регистрация: 14.11.2003
Где: Львов

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



sTa1kEr, 
Цитата

ST_Falcon, В принципе можно вытащить строки из рандомно отсортированной таблицы images с группировкой по id категории, как и предложил SelenIT, но, имхо, это плохой стиль (поле id картинки будет без группировки и без вычисляемой функции)...

пробовал. в таблице изображений 20000 записей. запрос с сортировкой по RAND() выполнялся около минуты...

Цитата

Ну и еще вариант. Добавить для таблицы картинок некое поле order, которое будет пронумеровать строки с сортировкой по id категории при каждом изменении таблицы images (или каждый раз пронумеровать при помощи переменной). Тогда можно будет одним запросом с группировкой выбрать случайные картинки по алгоритму:
   FLOOR(MIN(`order`) + RAND() * (MAX(`order`) - MIN(`ORDER`)))  

не очень понял. что значит "которое будет пронумеровать строки с сортировкой по id категории при каждом изменении таблицы images (или каждый раз пронумеровать при помощи переменной)"?
делать еще один индекс картинок для каждой из категорий?
1я категория order от 0 до count(картинок в первой категории)
2z категория order от 0 до count(картинок в второй категории)
так?
PM MAIL ICQ   Вверх
sTa1kEr
Дата 17.10.2007, 20:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


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

Репутация: 7
Всего: 146



Цитата(ST_Falcon @  17.10.2007,  19:30 Найти цитируемый пост)
делать еще один индекс картинок для каждой из категорий?
1я категория order от 0 до count(картинок в первой категории)
2z категория order от 0 до count(картинок в второй категории)
так? 

Нет. Не совсем. 1я категория от 1 до count(картинок в первой категории). 2ая от count(картинок в первой категории) + 1 до count(картинок в второй категории) и т.д. Т.е. что бы таблица images была гарантированно пронумерована от 1 до count(всего картинок). Тогда при групповом запросе можно получить гарантированно существующий случайный номер строки.
PM MAIL   Вверх
sTa1kEr
Дата 18.10.2007, 09:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


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

Репутация: 7
Всего: 146



Код

SET @i = 0, @j = 0;
SELECT d.*
FROM (
      SELECT ROUND(MIN(`order`) + RAND() * (MAX(`order`) - MIN(`order`))) AS `img_order`
      FROM (
            SELECT i.`category_id`, @i:=@i+1 AS `order`
            FROM `table` i
            ORDER BY `category_id`
           ) img
      GROUP BY img.`category_id`
     ) rnd
INNER JOIN (
            SELECT i.*, @j:=@j+1 AS `img_order`
            FROM `table` i
            ORDER BY `category_id`
           ) d ON d.`img_order` = rnd.`img_order`;

Вот такой вот 3х этажный запрос получился. Если колонку `order` обновлять при каждом INSERT-е/UPDATE-е (можно на триггер повесить эту задачу), то SELECT очень сильно упростится - можно будет выкинуть два подзапроса и переменные.

Добавлено через 2 минуты и 43 секунды
Код

SELECT d.*
FROM (
      SELECT ROUND(MIN(`order`) + RAND() * (MAX(`order`) - MIN(`order`))) AS `img_order`
      FROM `table` img
      GROUP BY img.`category_id`
     ) rnd
INNER JOIN `table`d ON d.`img_order` = rnd.`img_order`;

Такой будет запрос если при обновлении простовлять `order`
PM MAIL   Вверх
muzer
Дата 19.10.2007, 03:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

Репутация: 30
Всего: 31



sTa1kEr, позволю заметить очень большой минус решения: если на триггер повесить обновление order, то это означает апдейт в среднем 50% строк при каждой вставке новой записи (в среднем вставляем в середину, значит двигаем всех кто выше), в решении без триггера - два раза сортировка всех строк при каждом select'е.

Хочу предложить тоже не идеальный, но вариант.

Таблицу images сделать примерно такой:

Код

CategoryID int NOT NULL default 0,
ImageID int NOT NULL auto_increment,
...
PRIMARY KEY (CategoryID, ImageID)


Запрос:
1) сначала вычисляется рэндом от или очень большого числа или от SELECT COUNT(*) FROM images. Операция пустяковая. 
2) 
Код

SELECT i.* FROM
( SELECT CategoryID, вычисленный_рэндом % MAX(ID) as ImageID FROM images GROUP BY CategoryID ) tmp
INNER JOIN
images i USING(CategoryID,ImageID)


Подзапрос выполняется достаточно быстро за счёт использования индекса, join соответственно тоже. 
PM WWW   Вверх
sTa1kEr
Дата 19.10.2007, 08:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


9/10 программиста
***


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

Репутация: 7
Всего: 146



Цитата(muzer @  19.10.2007,  03:00 Найти цитируемый пост)
sTa1kEr, позволю заметить очень большой минус решения: если на триггер повесить обновление order, то это означает апдейт в среднем 50% строк при каждой вставке новой записи (в среднем вставляем в середину, значит двигаем всех кто выше)

Расчет быд на то, что INSERT-ы *значительно* реже SELECT-ов.

Цитата(muzer @  19.10.2007,  03:00 Найти цитируемый пост)
в решении без триггера - два раза сортировка всех строк при каждом select'е.

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

Цитата(muzer @  19.10.2007,  03:00 Найти цитируемый пост)
Таблицу images сделать примерно такой

1. Тогда уж "вычисленный_рэндом % MAX(ID) + 1" иначе теряется первая картинка (вместе со всей категорией).
2. Мы жестко привязываемся к движку MyISAM со всеми вытекающими.
3. Если картинка будет удалена, то образуется "пустота". И если вычислить именно этот несуществующий ID, то мы при запросе потеряем всю категорию! Что *значительно* хуже, чем дополнительный апдейт на пол таблицы.

Но вы навели на мысль. Создаем примерно такой триггер на вставку
Код

CREATE TRIGGER `order_insert` BEFORE INSERT ON `images`
  FOR EACH ROW
  BEGIN
    SET NEW.`order` = (SELECT COUNT(*) FROM `ids` WHERE `images` = NEW.`category_id`);
  END;

А после удаления пересчитываем order для категории. И соответственно запрос будет
Код

SELECT i.*
FROM (
      SELECT ROUND(RAND() * MAX(`order`)) AS `order`
      FROM `images` img
      GROUP BY img.`category_id`
     ) rnd
INNER JOIN `images` i ON i.`order` = rnd.`order`;

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


 




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


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

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