基于多个条件的高度过滤搜索

时间:2010-07-01 06:07:54

标签: php mysql multiple-tables filtered-lookup

我在php中用餐馆搜索有两张桌子。关于餐厅类型,设施,美食的所有信息都参考表2中的餐馆ID进入表2中。我如何运行查询以便我可以获得所有服务于餐馆的餐厅。供应晚餐,还有停车位吗?

这似乎不起作用:

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 个答案:

答案 0 :(得分:1)

我知道做这样的事情的唯一方法是“转动”数据 - 基本上将行放入列中。例如。您目前每个值都有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 :(得分:1)

鉴于您现有的结构,这很容易:

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);

答案 2 :(得分:0)

试试这个..

SELECT r.name FROM restaurant as r JOIN stack as s ON r.id = s.rest_id WHERE s.value ='chinese'AND s.value ='dinner'AND s.value ='parking';