1

PHPで行われたレストラン検索に2つのテーブルがあります。レストランの種類、施設、料理に関するすべての情報は、表 1 のレストラン 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

これが間違った方法かどうかも教えてください。私はスタックを使用しました。料理、施設はすべて定義されておらず、それぞれに非常に無制限であるためです。

4

3 に答える 3

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);
于 2010-07-01T11:54:02.360 に答える
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
于 2010-07-01T07:30:18.687 に答える
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';

于 2010-07-01T06:24:41.860 に答える