【问题标题】:More Efficient MySQL Subquery?更高效的 MySQL 子查询?
【发布时间】:2013-11-13 04:09:03
【问题描述】:

我在用子查询来处理查询时遇到了麻烦。如果愿意,请继续:

样本数据:

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 吉普车的人所拥有的其他汽车。

有什么建议吗?

【问题讨论】:

标签: mysql subquery group-concat having-clause


【解决方案1】:

因为这是一个多对多的关系,你实际上需要将cars 分成两个表。第一个表将与现在的cars 相同,除了从中删除user_id。第二个表(称为user_car)有两列:user_id 和car_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
  ))

【讨论】:

  • 这更加规范化,但我正在处理有限的数据集。 cars 表不包含所有可能的年份、品牌、型号组合。考虑到这一点,您还会推荐它吗?
  • 很少有标准化没有意义的情况,我认为这不是其中之一。汽车类型的存在并不取决于是否有人驾驶它,因此为汽车提供自己的桌子是有意义的。我也怀疑你会受到任何明显的性能影响。
【解决方案2】:

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 

【讨论】:

    【解决方案3】:

    为拥有 Jeep 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;
    

    【讨论】:

    • 这非常完美。我必须将我的大脑旋转 90 度才能理解它。那里的“AS j”是干什么用的?
    • @pbarney,在 SQL 中,任何派生表子查询都需要表别名。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 2011-09-03
    • 1970-01-01
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多