質問

I'm trying to filter using a dynamic key into PivotTable.

I have 3 tables, defined below. When I don't use group by, my query uses a LEFT JOIN statement and results in something like this:

Example 1
(source: maverabilisim.com)

Yellow bars are to-columns and the blue bar is the filter condition.

I need it to return something looking like this:

Example 2
(source: maverabilisim.com)

using a condition like this: .... AND v.key_id=1 (color=1)

How do I build SQL query to reach this result?

My SQL schema/test data:

CREATE TABLE `ads` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `ads_title` CHAR(80) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)

);


CREATE TABLE `ads_keys` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `key` CHAR(25) NULL DEFAULT NULL ,
    `inlist` INT(1) UNSIGNED ZEROFILL NULL DEFAULT NULL ,
    PRIMARY KEY (`id`)
);
CREATE TABLE `ads_values` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `ads_id` INT(3) NULL DEFAULT NULL,
    `key_id` INT(10) NULL DEFAULT NULL,
    `value` INT(10) NULL DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE INDEX `ads_id` (`ads_id`, `key_id`)
);
INSERT INTO `ads` VALUES (1, 'AAA');
INSERT INTO `ads` VALUES (2, 'BBB');
INSERT INTO `ads` VALUES (3, 'CCC');
INSERT INTO `ads` VALUES (4, 'DDD');
INSERT INTO `ads` VALUES (5, 'EEE');
INSERT INTO `ads` VALUES (6, 'FFF');
INSERT INTO `ads_keys` VALUES (1, 'KM', 1);
INSERT INTO `ads_keys` VALUES (2, 'OIL', 0);
INSERT INTO `ads_keys` VALUES (3, 'YEAR', 1);
INSERT INTO `ads_keys` VALUES (4, 'COLOR', 0);
INSERT INTO `ads_keys` VALUES (5, 'SPEED', 0);

INSERT INTO `ads_values` VALUES (1, 1, 1, 89000);
INSERT INTO `ads_values` VALUES (2, 1, 2, 200);
INSERT INTO `ads_values` VALUES (3, 1, 3, 2010);
INSERT INTO `ads_values` VALUES (4, 1, 4, 1);
INSERT INTO `ads_values` VALUES (5, 1, 5, 180);
INSERT INTO `ads_values` VALUES (6, 2, 1, 13000);
INSERT INTO `ads_values` VALUES (7, 2, 2, 150);
INSERT INTO `ads_values` VALUES (8, 2, 3, 2008);
INSERT INTO `ads_values` VALUES (9, 2, 4, 1);
INSERT INTO `ads_values` VALUES (10, 2, 5, 160);
INSERT INTO `ads_values` VALUES (11, 3, 1, 79800);
INSERT INTO `ads_values` VALUES (12, 3, 2, 172);
INSERT INTO `ads_values` VALUES (13, 3, 3, 2008);
INSERT INTO `ads_values` VALUES (14, 3, 4, 2);
INSERT INTO `ads_values` VALUES (15, 3, 5, 178);
INSERT INTO `ads_values` VALUES (16, 4, 1, 56781);
INSERT INTO `ads_values` VALUES (17, 4, 2, 127);
INSERT INTO `ads_values` VALUES (18, 4, 3, 2009);
INSERT INTO `ads_values` VALUES (19, 4, 4, 3);
INSERT INTO `ads_values` VALUES (20, 4, 5, 156);
INSERT INTO `ads_values` VALUES (21, 5, 1, 10200);
INSERT INTO `ads_values` VALUES (22, 5, 2, 205);
INSERT INTO `ads_values` VALUES (23, 5, 3, 2000);
INSERT INTO `ads_values` VALUES (24, 5, 4, 3);
INSERT INTO `ads_values` VALUES (25, 5, 5, 160);
INSERT INTO `ads_values` VALUES (26, 6, 1, 45877);
INSERT INTO `ads_values` VALUES (27, 6, 2, 150);
INSERT INTO `ads_values` VALUES (28, 6, 3, 2009);
INSERT INTO `ads_values` VALUES (29, 6, 4, 1);
INSERT INTO `ads_values` VALUES (30, 6, 5, 168);
役に立ちましたか?

解決

Try

SELECT a.id, a.ads_title,
       MIN(CASE WHEN v.key_id = 1 THEN v.value END) `km`,
       MIN(CASE WHEN v.key_id = 3 THEN v.value END) `year`
  FROM ads_values v JOIN ads a
    ON v.ads_id = a.id 
 -- WHERE a.id = 2 -- if you need to fetch an ad with a particular id
 GROUP BY a.id, a.ads_title
HAVING SUM(CASE WHEN v.key_id = 4 AND v.value = 1 THEN 1 ELSE 0 END) > 0 

Sample output:

| ID | ADS_TITLE |    KM | YEAR |
---------------------------------
|  1 |       AAA | 89000 | 2010 |
|  2 |       BBB | 13000 | 2008 |
|  6 |       FFF | 45877 | 2009 |

Here is SQLFiddle demo

他のヒント

SELECT ads.id, ads.ads_title, `km`.value AS `KM`, `year`.value AS `YEAR`
FROM ads
JOIN ads_values AS `color` ON (`color`.ads_id, `color`.key_id) = (ads.id, 4)
JOIN ads_values AS `km`    ON (`km`.ads_id, `km`.key_id)       = (ads.id, 1)
JOIN ads_values AS `year`  ON (`year`.ads_id, `year`.key_id)   = (ads.id, 3)
WHERE ads.id = 2 AND `color`.value = 1

Re your comment, I think you are asking if this query works well when the table has many rows.

Here's the output of EXPLAIN, showing that it uses indexes for each of the joins. The other thing to look for is rows which is the estimate of rows to examine. The numbers are small in the example below, but that might be because we have only a small number of rows in the table.

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: ads
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: color
         type: const
possible_keys: ads_id
          key: ads_id
      key_len: 10
          ref: const,const
         rows: 1
        Extra: NULL
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: km
         type: const
possible_keys: ads_id
          key: ads_id
      key_len: 10
          ref: const,const
         rows: 1
        Extra: NULL
*************************** 4. row ***************************
           id: 1
  select_type: SIMPLE
        table: year
         type: const
possible_keys: ads_id
          key: ads_id
      key_len: 10
          ref: const,const
         rows: 1
        Extra: NULL

For comparison, I ran EXPLAIN on the query in @peterm's answer. His query has fewer joins, but his query creates a temporary table. This is usually bad for performance.

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: a
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: v
         type: ref
possible_keys: ads_id,covering
          key: covering
      key_len: 5
          ref: const
         rows: 5
        Extra: Using index

EXPLAIN is only an estimate. You should test both queries with your real data.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top