2013-11-01 71 views
1

我在用子查询打包查询时遇到了麻烦。请跟随,如果你会:更高效的MySQL子查询?

示例数据:

CREATE TABLE users (
    user_id INT(10) NOT NULL AUTO_INCREMENT, 
    username VARCHAR(64), 
    PRIMARY KEY (user_id) 
); 

CREATE TABLE cars { 
    car_id INT(10) NOT NULL AUTO_INCREMENT, 
    user_id INT(10), 
    caryear YEAR(4), 
    carmaker VARCHAR(32), 
    carmodel VARCHAR(32), 
    PRIMARY KEY (car_id, user_id) 
    FOREIGN KEY(`user_id`) references users(`user_id`) 
) ENGINE=InnoDB;  

INSERT INTO users (user_id, name) VALUES (1,'Bob'),(2,'John'),(3,'Sally'); 
INSERT INTO cars (user_id, caryear, carmaker, carmodel) VALUES 
(1,'2004','Audi','A4'), 
(1,'2006','Toyota','Camry'), 
(1,'2014','Jeep','CJ'), 
(2,'1998','Acura','CL'), 
(2,'2014','Honda','Accord'), 
(3,'2011','Jeep','Rubicon') 

好了,所以如果我要得到用户和他们的汽车名单,我可以做这样的事情:

SELECT 
    user_id, 
    username, 
    (SELECT GROUP_CONCAT(CONCAT(CarYear,' ',CarMaker,' ',CarModel)) 
    FROM cars 
    WHERE cars.user_id = users.user_id 
) AS Cars 
FROM users; 

但是,如何获得拥有2004-2014吉普车的用户列表?

我已经想出了这一点,但我敢肯定有一个更优雅的方式

SELECT 
    user_id, 
    username, 
    (SELECT GROUP_CONCAT(CONCAT(CarYear,' ',CarMaker,' ',CarModel)) 
    FROM cars 
    WHERE cars.user_id = users.user_id 
    AND CarMaker = 'Jeep' 
    AND CarYear BETWEEN 2004 AND 2014 
) AS Cars 
FROM users 
HAVING Cars is not null; 

这个解决方案的问题是,它不显示其他车辆归拥有2004 - 2014年吉普车的人。

有什么建议吗?

+0

注GROUP_CONCAT有1024个字节的限制。 。就像文档http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat中提到的很可能这是导致你的问题.. –

回答

1

要显示的用户和他们的所有汽车的列表,谁拥有一辆吉普2004-2014任何用户:

SELECT 
    u.user_id, 
    u.username, 
    GROUP_CONCAT(CONCAT(c.CarYear,' ',c.CarMaker,' ',c.CarModel)) AS Cars 
FROM users u 
INNER JOIN cars c USING (user_id) 
INNER JOIN (
    SELECT DISTINCT user_id FROM cars 
    WHERE c.CarMaker = 'Jeep' 
    AND c.CarYear BETWEEN 2004 AND 2014) AS j USING (user_id) 
GROUP BY u.user_id, u.username; 
+0

这完全是完美的。我必须旋转我的大脑90度才能理解它。什么是“AS j”? – pbarney

+0

@pbarney,在SQL中,任何派生表子查询都需要一个表别名。 –

0

因为这是一个多对多的关系,所以实际上需要将cars分成两个表。第一个表格将与cars一样,除了从中删除user_id。第二个表(称为user_car)有两列:user_idcar_id

一旦你这样做,查询很简单:

SELECT user_id FROM user_car WHERE car_id IN 
    (SELECT car_id FROM cars 
    WHERE CarMaker = 'Jeep' 
    AND CarYear BETWEEN 2004 AND 2014 
) 

如果需要直接从users提取数据,展开查询:

SELECT user_id, username FROM users WHERE user_id IN 
    (SELECT user_id FROM user_car WHERE car_id IN 
    (SELECT car_id FROM cars 
    WHERE CarMaker = 'Jeep' 
    AND CarYear BETWEEN 2004 AND 2014 
)) 
+0

这是更正常化,但我正在处理有限的数据集。 “汽车”表不包含所有可能的年份,品牌,型号组合。考虑到这一点,你还会推荐它吗? – pbarney

+0

很少有正常化没有意义的情况,我不认为这是其中之一。汽车类型的存在并不取决于是否有人驾驶它,所以给汽车自己的桌子是有道理的。我也怀疑你会有明显的表现。 –

0

INNER JOIN会做的伎俩。

SELECT u.* FROM users u 
INNER JOIN cars c ON c.user_id = u.user_id 
WHERE CarMaker = 'Jeep' AND CarYear BETWEEN 2004 AND 2014 

inner join