【问题标题】:Highly filtered search based on multiple conditions基于多个条件的高度过滤搜索
【发布时间】:2010-07-01 06:07:54
【问题描述】:

我在用 php 完成的餐厅搜索中有两张桌子。所有关于餐厅类型、设施、菜式的信息都输入到 Table2 -'stack' 中,参考他们在表 1 中的餐厅 ID。我如何运行查询,以便我可以获得所有提供中国菜的餐厅以及供应晚餐,也有停车位?

这似乎不起作用:

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

还告诉我这是否是错误的方法。我使用堆栈,因为美食,设施都可以不受限制,因为它没有定义并且非常适合每个人。

【问题讨论】:

    标签: php mysql multiple-tables filtered-lookup


    【解决方案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) 的数值与您要搜索的值的数量相匹配。这是一个简单的测试用例(注意我添加了另一家餐厅,实际上有'chinese'、'dinner'和'parking':

    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】:

      我知道做这样的事情的唯一方法是“透视”数据——本质上是把行变成列。例如。目前,您的每个值都有 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
      

      【讨论】:

        【解决方案3】:

        试试这个..

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

        【讨论】:

          猜你喜欢
          • 2022-07-21
          • 2021-12-14
          • 2012-02-12
          • 2011-09-06
          • 1970-01-01
          • 2019-09-10
          • 2017-06-08
          • 2018-06-12
          • 1970-01-01
          相关资源
          最近更新 更多