Высокофильтрованный поиск по нескольким условиям - PullRequest
1 голос
/ 01 июля 2010

У меня есть две таблицы в поиске ресторана в php. Вся информация о типе ресторана, заведениях, кухне вводится в Table2 -'stack со ссылкой на их идентификатор ресторана в таблице 1. Как мне выполнить запрос, чтобы я мог получить все рестораны, которые подают китайский, а также подают ужин, а также есть парковка?

Это не похоже на работу:

SELECT DISTINCT restaurant.name, restaurant.place 
FROM stack,restaurant 
WHERE restaurant.id=stack.rest_id AND stack.value='chineese' 
      AND  stack.value='dinner' AND  stack.value='parking'

Вот моя структура таблицы

Table1 - **restaurant**
------+----------+----------
  id  +   name   +   place
------+----------+----------
   1      rest1       ny
   2      rest2       la
   3      rest3       ph
   4      rest4       mlp




Table2 - **stack**
------+----------+-------------------------
  id  + rest_id  +     type      +  value 
------+----------+-------------------------
   1      1          cuisine      chinese
   2      1          serves       breakfast
   3      1          facilities   party hall
   4      1          serves       lunch
   5      1          serves       dinner
   6      1          cuisine      seafood
   7      2          cuisine      Italian
   8      2          serves       breakfast
   9      2          facilities   parking
   10     2          serves       lunch
   11     2          serves       dinner
   12     2          cuisine      indian

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

Ответы [ 3 ]

1 голос
/ 01 июля 2010

Единственный способ, которым я знаю, что-то подобное сделать, - это «повернуть» данные - по сути, превратив строки в столбцы. Например. В настоящее время у вас есть 1 строка для каждого из ваших значений, но в идеале вам нужна 1 строка для каждого ресторана, чтобы вы могли запросить значения.

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

Следующее должно дать представление о том, как создать сводку:

SELECT        
  rest_id, 
  MAX(CASE WHEN s.value = 'chinese' THEN 1 ELSE 0 END) AS chinese, 
  MAX(CASE WHEN s.value = 'breakfast' THEN 1 ELSE 0 END) AS breakfast, 
  MAX(CASE WHEN s.value = 'party hall' THEN 1 ELSE 0 END) AS [party hall], 
  MAX(CASE WHEN s.value = 'lunch' THEN 1 ELSE 0 END) AS lunch,
  MAX(CASE WHEN s.value = 'dinner' THEN 1 ELSE 0 END) AS dinner, 
  MAX(CASE WHEN s.value = 'seafood' THEN 1 ELSE 0 END) AS seafood,
  MAX(CASE WHEN s.value = 'Italian' THEN 1 ELSE 0 END) AS Italian,  
  MAX(CASE WHEN s.value = 'parking' THEN 1 ELSE 0 END) AS parking, 
  MAX(CASE WHEN s.value = 'Indian' THEN 1 ELSE 0 END) AS indian 
FROM            
  stack AS s
GROUP BY 
  rest_id

Это создаст таблицу, которая выглядит следующим образом:

rest_id | chinese | breakfast | party hall | lunch | dinner | seafood | Italian | parking | indian
--------+---------+-----------+------------+-------+--------+---------+---------+---------+-------
   1    |   1     |     1     |      1     |   1   |    1   |    1    |    0    |    0    |    0
   2    |   0     |     1     |      0     |   1   |    1   |    0    |    1    |    1    |    1

Из этой таблицы довольно просто объединить рестораны с особыми особенностями.

Например:

SELECT restaurant.name, restaurant.place FROM restaurant LEFT JOIN
  (SELECT        
    rest_id, 
    MAX(CASE WHEN s.value = 'chinese' THEN 1 ELSE 0 END) AS chinese, 
    MAX(CASE WHEN s.value = 'breakfast' THEN 1 ELSE 0 END) AS breakfast, 
    MAX(CASE WHEN s.value = 'party hall' THEN 1 ELSE 0 END) AS [party hall], 
    MAX(CASE WHEN s.value = 'lunch' THEN 1 ELSE 0 END) AS lunch,
    MAX(CASE WHEN s.value = 'dinner' THEN 1 ELSE 0 END) AS dinner, 
    MAX(CASE WHEN s.value = 'seafood' THEN 1 ELSE 0 END) AS seafood,
    MAX(CASE WHEN s.value = 'Italian' THEN 1 ELSE 0 END) AS Italian,  
    MAX(CASE WHEN s.value = 'parking' THEN 1 ELSE 0 END) AS parking, 
    MAX(CASE WHEN s.value = 'Indian' THEN 1 ELSE 0 END) AS indian 
  FROM            
    stack AS s
  GROUP BY 
    rest_id) AS features
ON 
  restaurant.id=features.rest_id
WHERE 
  features.chinese=1 and features.dinner=1 and features.parking=1
1 голос
/ 01 июля 2010

Учитывая вашу существующую структуру, это довольно просто:

SELECT name, place FROM restaurant WHERE id IN (
    SELECT rest_id FROM stack
    WHERE value IN ('chinese', 'dinner', 'parking')
    GROUP BY rest_id
HAVING COUNT(rest_id)=3);

Просто убедитесь, что числовое значение, присвоенное HAVING COUNT(rest_id), соответствует количеству значений, которые вы ищете.Вот простой тестовый пример (обратите внимание, что я добавил еще один ресторан, в котором на самом деле есть «китайский», «ужин» и «парковка»):

CREATE TABLE `restaurant` (
  `id` int(11) NOT NULL auto_increment,
  `name` VARCHAR(255),
  `place` VARCHAR(255),
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

CREATE TABLE `stack` (
  `id` int(11) NOT NULL auto_increment,
  `rest_id` int(11) NOT NULL,
  `type` VARCHAR(255),
  `value` VARCHAR(255),
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

INSERT INTO `restaurant` VALUES
    (1, 'rest1', 'ny'),
    (2, 'rest2', 'la'),
    (3, 'rest3', 'ph'),
    (4, 'rest4', 'mlp');

INSERT INTO `stack` VALUES
    ( 1, 1, 'cuisine',    'chinese'),
    ( 2, 1, 'serves',     'breakfast'),
    ( 3, 1, 'facilities', 'party hall'),
    ( 4, 1, 'serves',     'lunch'),
    ( 5, 1, 'serves',     'dinner'),
    ( 6, 1, 'cuisine',    'seafood'),
    ( 7, 2, 'cuisine',    'Italian'),
    ( 8, 2, 'serves',     'breakfast'),
    ( 9, 2, 'facilities', 'parking'),
    (10, 2, 'serves',     'lunch'),
    (11, 2, 'serves',     'dinner'),
    (12, 2, 'cuisine',    'indian'),
    (13, 3, 'cuisine',    'chinese'),
    (14, 3, 'serves',     'breakfast'),
    (15, 3, 'facilities', 'parking'),
    (16, 3, 'serves',     'lunch'),
    (17, 3, 'serves',     'dinner'),
    (18, 3, 'cuisine',    'indian');

SELECT name, place FROM restaurant WHERE id IN (
    SELECT rest_id FROM stack
    WHERE value IN ('chinese', 'dinner', 'parking')
    GROUP BY rest_id
HAVING COUNT(rest_id)=3);

+-------+-------+
| name  | place |
+-------+-------+
| rest3 | ph    |
+-------+-------+

SELECT name, place FROM restaurant WHERE id IN (
    SELECT rest_id FROM stack
    WHERE value IN ('chinese', 'dinner')
    GROUP BY rest_id
HAVING COUNT(rest_id)=2);

+-------+-------+
| name  | place |
+-------+-------+
| rest1 | ny    |
| rest3 | ph    |
+-------+-------+

SELECT name, place FROM restaurant WHERE id IN (
    SELECT rest_id FROM stack
    WHERE value IN ('parking', 'hellipad')
    GROUP BY rest_id
HAVING COUNT(rest_id)=2);

Empty set (0.00 sec)

В качестве альтернативы, вы можете создать связанные таблицы, как это (ноэто, вероятно, не лучшая структура):

                                            ---> facility
restaurant ---> restaurant_has_facility ---|
                                            ---> facility_type

Запрос почти такой же, вам просто нужен ваш подзапрос для создания соответствующего объединения:

SELECT restaurant_name, restaurant_place FROM (
    SELECT
        r.id AS restaurant_id,
        r.name AS restaurant_name,
        r.place AS restaurant_place,
        ft.name AS facility_name
    FROM restaurant AS r
    JOIN restaurant_has_facility AS rf ON rf.restaurant_id = r.id
    JOIN facility_type AS ft ON ft.id = rf.facility_type_id
    ORDER BY r.id, ft.name) AS tmp
WHERE facility_name IN ('chinese', 'dinner', 'parking')
GROUP BY tmp.restaurant_id
HAVING COUNT(tmp.restaurant_id)=3;

Вот пример SQL длявышеуказанная структура:

CREATE TABLE `restaurant` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NOT NULL ,
  `place` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;

CREATE TABLE `facility` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;

CREATE  TABLE IF NOT EXISTS `facility_type` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;

CREATE  TABLE IF NOT EXISTS `restaurant_has_facility` (
  `restaurant_id` INT UNSIGNED NOT NULL ,
  `facility_id` INT UNSIGNED NOT NULL ,
  `facility_type_id` INT UNSIGNED NOT NULL ,
  PRIMARY KEY (`restaurant_id`, `facility_id`, `facility_type_id`) ,
  INDEX `fk_restaurant_has_facility_restaurant` (`restaurant_id` ASC) ,
  CONSTRAINT `fk_restaurant_has_facility_restaurant`
    FOREIGN KEY (`restaurant_id` )
    REFERENCES `restaurant` (`id` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;

INSERT INTO `restaurant` VALUES
    (1, 'rest1', 'ny'),
    (2, 'rest2', 'la'),
    (3, 'rest3', 'ph'),
    (4, 'rest4', 'mlp');

INSERT INTO `facility` VALUES
    (1, 'cuisine'),
    (2, 'serves'),
    (3, 'facilities');

INSERT INTO `facility_type` VALUES
    (1, 'chinese'),
    (2, 'breakfast'),
    (3, 'party hall'),
    (4, 'lunch'),
    (5, 'dinner'),
    (6, 'seafood'),
    (7, 'Italian'),
    (8, 'parking'),
    (9, 'indian');

INSERT INTO `restaurant_has_facility` VALUES
    (1, 1, 1),
    (1, 2, 2),
    (1, 3, 3),
    (1, 2, 4),
    (1, 2, 5),
    (1, 1, 6),
    (2, 1, 7),
    (2, 2, 2),
    (2, 3, 8),
    (2, 2, 4),
    (2, 2, 5),
    (2, 1, 9),
    (3, 1, 1),
    (3, 2, 5),
    (3, 3, 8),
    (3, 2, 4),
    (3, 2, 2),
    (3, 1, 9);
0 голосов
/ 01 июля 2010

попробуйте это ..

ВЫБЕРИТЕ r.name ИЗ ресторана как r СОЕДИНЯЙТЕ стек как s ON r.id = s.rest_id ГДЕ s.value = 'китайский' И s.value = 'ужин' Иs.value = 'парковка';

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...