Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
C++. Мастер-класс в задачах и примерах. Авторы: Кузнецов М.В., Симдянов И.В. MySQL на примерах. Авторы: Кузнецов М.В., Симдянов И.В. PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. PHP Puzzles. Авторы: Кузнецов М.В., Симдянов И.В. PHP 5/6. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум MySQL

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Застрял с выборкой: 3 таблицы + many-to-many
 
 автор: Sync   (08.08.2006 в 02:18)   письмо автору
 
 

Суть вроде проста. Есть 3 таблицы: таблица номеров (rooms), таблица возрастных категорий (ages) и таблица связки номеров и возр. категорий (rooms_ages) - то есть, какие возрастные категории могут размещаться в номерах.

Таблица rooms:

+--------------+-----------------------+------+-----+---------+----------------+
| Field        | Type                  | Null | Key | Default | Extra          |
+--------------+-----------------------+------+-----+---------+----------------+
| room_id      | int(11)               |      | PRI | NULL    | auto_increment |
| name         | varchar(255)          |      |     |         |                |
+--------------+-----------------------+------+-----+---------+----------------+


Таблица ages (здесь age_start, age_end - возраст от и до):

+-----------+-------------+------+-----+---------+----------------+
| Field     | Type        | Null | Key | Default | Extra          |
+-----------+-------------+------+-----+---------+----------------+
| age_id    | int(11)     |      | PRI | NULL    | auto_increment |
| age_start | tinyint(4)  |      |     | 0       |                |
| age_end   | tinyint(4)  |      |     | 0       |                |
+-----------+-------------+------+-----+---------+----------------+


И таблица связки rooms_ages:

+----------+---------+------+-----+---------+-------+
| Field    | Type    | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| room_id  | int(11) |      | MUL | 0       |       |
| age_id   | int(11) |      | MUL | 0       |       |
+----------+---------+------+-----+---------+-------+


На входе имеем массив возрастов (количество неизвестно), на пальцах пусть 16 и 25 лет.
Нужно отобрать все комнаты, которые могут принять этих двух поселенцев.

Полдня попыток приводили к каким-то запросам, но не совсем верным. На чем остановился:

SELECT r.* , COUNT(*) AS total
FROM rooms r, ages a1, ages a2, rooms_ages ra1, rooms_ages ra2

WHERE
(
     r.room_id = ra1.room_id
     AND ra1.age_id = a1.age_id
     AND (
          a1.age_start <=16
          AND a1.age_end >=16
          )
)

AND

(
     r.room_id = ra2.room_id
     AND ra2.age_id = a2.age_id
     AND (
          a2.age_start <=25
          AND a2.age_end >=25
          )
)

GROUP BY room_id


То есть кол-во таблиц (aN, raN) == количеству поселенцев.

Среди результатов запроса есть правильные данные, но есть и левые, причем total по логике не совсем верный выходит.
Вобщем что-то не так, а что - не пойму.
Да, кстати выбрать сначала все age_id, а потом сделать что-то типа WHERE ... IN () не подходит.

Буду ОЧЕНЬ благодарен за помощь. Если нужны дампы - выложу.

   
 
 автор: Trianon   (08.08.2006 в 09:57)   письмо автору
 
   для: Sync   (08.08.2006 в 02:18)
 

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

   
 
 автор: Sync   (08.08.2006 в 13:30)   письмо автору
 
   для: Trianon   (08.08.2006 в 09:57)
 

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

   
 
 автор: Trianon   (08.08.2006 в 14:27)   письмо автору
 
   для: Sync   (08.08.2006 в 13:30)
 

Так это и есть - любой (каждый) из указанный.

Как-то так:

$list = '16,25'; // = implode(',',array(16,25))
$sql = "SELECT r.name, a.age_start, a.age_end
FROM rooms r, ages a, rooms_ages ra
WHERE r.room_id = ra.room_id AND ra.age_id = a.age_id
AND age_start <= min($list) AND age_end >= max($list)";

   
 
 автор: Sync   (08.08.2006 в 15:36)   письмо автору
 
   для: Trianon   (08.08.2006 в 14:27)
 

Немного не то... Дело в том, что отношение между таблицами rooms и ages много-ко-многим.
Он вернет результаты, если двое наших поселенцев находятся в ОДНОЙ возрастной категории, если же нет (например 5 лет и 20 лет) - будет empty set.

   
 
 автор: Trianon   (08.08.2006 в 16:10)   письмо автору
 
   для: Sync   (08.08.2006 в 15:36)
 

Показывайте дамп таблиц, пример запроса и ожидаемый результат. Так яснее будет.
(дамп либо в аттаче, либо без обратных косых, т.к. последние тут в репликах искажаются)

   
 
 автор: Sync   (08.08.2006 в 16:39)   письмо автору
 
   для: Trianon   (08.08.2006 в 16:10)
 


CREATE TABLE 'ages' (
  'age_id' int(11) NOT NULL auto_increment,
  'age_start' tinyint(4) NOT NULL default '0',
  'age_end' tinyint(4) NOT NULL default '0',
  PRIMARY KEY  ('age_id')
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

INSERT INTO 'ages' VALUES (1, 2, 15);
INSERT INTO 'ages' VALUES (2, 16, 30);

-- --------------------------------------------------------


CREATE TABLE 'rooms' (
  'room_id' int(10) unsigned NOT NULL auto_increment,
  'name' varchar(255) NOT NULL default '',
  PRIMARY KEY  ('room_id')
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;


INSERT INTO 'rooms' VALUES (1, 'deluxe');
INSERT INTO 'rooms' VALUES (2, 'bungalo');

-- --------------------------------------------------------

CREATE TABLE 'rooms_ages' (
  'room_id' int(11) NOT NULL default '0',
  'age_id' int(11) NOT NULL default '0'
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

INSERT INTO 'rooms_ages' VALUES (1, 1);
INSERT INTO 'rooms_ages' VALUES (1, 2);
INSERT INTO 'rooms_ages' VALUES (2, 1);
INSERT INTO 'rooms_ages' VALUES (2, 1);



При таких данных ваш запрос:

SELECT r.name, a.age_start, a.age_end 
FROM rooms r, ages a, rooms_ages ra 
WHERE r.room_id = ra.room_id AND ra.age_id = a.age_id 
AND age_start <= 4 AND age_end >= 20


должен вернуть комнату deluxe (room_id=1), так как есть записи:

room_id age_id
1                  1
1                  2

то есть она может селить как от 2 до 15, так и от 16 до 30. А в результате ряды не находятся.

   
 
 автор: Trianon   (08.08.2006 в 19:53)   письмо автору
 
   для: Sync   (08.08.2006 в 16:39)
 

Тяжелая задачка. В особенности если INTERSECT нету.
Я добавил таблицу гостей, которых надо селить, с их возрастами guests (guest_id, guest_age)
тогда стал возможен (на 4.1 и выше) такой запрос.


SELECT room_id FROM (SELECT DISTINCT guest_id, room_id 
FROM rooms_ages ra JOIN ages a ON ra.age_id=a.age_id 
JOIN guests g ON g.age BETWEEN a.age_start AND a.age_end ) AS RES 
GROUP BY room_id HAVING COUNT(guest_id) = (SELECT COUNT(*) FROM guests)

   
 
 автор: Sync   (08.08.2006 в 21:33)   письмо автору
 
   для: Trianon   (08.08.2006 в 19:53)
 

Если вставить в guests возрысты 12 и 13 лет например (первая возрастная категория), то в итоге получим как room_id=1 так и room_id=2, а надо тока room_id=2, так как только она у нас принимает двух человек с первой возр. категории. Может чето поправить надо, щас буду пробовать.

   
 
 автор: Trianon   (09.08.2006 в 10:07)   письмо автору
 
   для: Sync   (08.08.2006 в 21:33)
 

>а надо тока room_id=2, так как только она у нас принимает двух человек с первой возр. категории.

это c каких гвоздей?
собственно, количество человек одной категорией ничем не ограничивается. В таблице даже нет такого поля.

   
 
 автор: Sync   (09.08.2006 в 21:36)   письмо автору
 
   для: Trianon   (09.08.2006 в 10:07)
 

Еще раз..
Записи :

room_id age_id 
1                  1 
1                  2 
2                  1 
2                  1 


означают что комната1 может поселить одного человека от 2 до 15 и еще одного такого же.
а комната2 - одного от 2 до 15 и одного от 16 до 30. никак иначе.
К нам пришло 2 человека по 12 и 13 лет => подходит только комната2.

Да, я понял что вас сбило. Прошу прощения.
Условие - всех пришедших селим в ОДНУ комнату.

   
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования