【问题标题】:MySQL SELECT Vehicle fitments selecting and filteringMySQL SELECT 车辆配件选择和过滤
【发布时间】:2015-05-10 18:54:13
【问题描述】:

我正在尝试实现车辆零件产品过滤,其中您有属于许多不同fitmentsproducts。比如一个特定的灯泡可以属于雪佛兰、福特和本田汽车。

我遇到的问题是使用 AND 和 OR 只是没有按照我的需要工作。

这是我正在寻找的结果的代码(表格结构和数据在底部):

SELECT * FROM products
JOIN fitments ON fitments.sku = products.sku

WHERE ANY fitments.make (grouped by sku) MATCHES ALL 'Ford' AND 'Chevrolet' AND 'Honda'

AND products.active = 1
AND fitments.cat_name = 'Cars'
GROUP BY products.sku

福特、雪佛兰和本田不在同一行,所以我不能把它变成 WHERE 子句?

OR 不起作用,因为我不想返回不符合所有条件的结果。

AND 不起作用,因为并非每一行都包含所有三个 makes 并且会返回 0 行。

我想要达到的结果:

我需要帮助的查询结果将返回一个products 行(不是fitments,因为fitments 只是关系查找),BULB2 因为它匹配所有三个make 参数(福特,雪佛兰和本田)从可能的配件。

两张桌子,产品和配件:

CREATE TABLE `fitments` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `cat_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `sku` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL,
  `make` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `model` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `start_year` varchar(4) COLLATE utf8_unicode_ci DEFAULT NULL,
  `end_year` varchar(4) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30211 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `products` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `sku` varchar(14) COLLATE utf8_unicode_ci DEFAULT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `active` tinyint(1) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=63730 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `fitments` (`id`, `cat_name`, `sku`, `make`, `model`, `start_year`, `end_year`, `created_at`, `updated_at`) VALUES ('1', 'Cars', 'BULB1', 'Ford', 'F150', '2013', '2015', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `fitments` (`id`, `cat_name`, `sku`, `make`, `model`, `start_year`, `end_year`, `created_at`, `updated_at`) VALUES ('2', 'Cars', 'BULB2', 'Ford', 'Explorer', '2013', '2015', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `fitments` (`id`, `cat_name`, `sku`, `make`, `model`, `start_year`, `end_year`, `created_at`, `updated_at`) VALUES ('3', 'Cars', 'BULB1', 'Ford', 'Mustang', '2013', '2015', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `fitments` (`id`, `cat_name`, `sku`, `make`, `model`, `start_year`, `end_year`, `created_at`, `updated_at`) VALUES ('4', 'Cars', 'BULB2', 'Chevrolet', 'Cobalt', '2013', '2015', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `fitments` (`id`, `cat_name`, `sku`, `make`, `model`, `start_year`, `end_year`, `created_at`, `updated_at`) VALUES ('5', 'Cars', 'BULB3', 'Chevrolet', 'Corvette', '2013', '2015', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `fitments` (`id`, `cat_name`, `sku`, `make`, `model`, `start_year`, `end_year`, `created_at`, `updated_at`) VALUES ('6', 'Cars', 'BULB2', 'Honda', 'Civic', '2013', '2015', '0000-00-00 00:00:00', '0000-00-00 00:00:00');


INSERT INTO `products` (`id`, `sku`, `name`, `active`, `created_at`, `updated_at`) VALUES ('1', 'BULB1', 'Generic Bulb 1', '1', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `products` (`id`, `sku`, `name`, `active`, `created_at`, `updated_at`) VALUES ('2', 'BULB2', 'Generic Bulb 2', '1', '0000-00-00 00:00:00', '0000-00-00 00:00:00');
INSERT INTO `products` (`id`, `sku`, `name`, `active`, `created_at`, `updated_at`) VALUES ('3', 'BULB3', 'Generic Bulb 3', '1', '0000-00-00 00:00:00', '0000-00-00 00:00:00');

【问题讨论】:

  • 您的预期结果是什么?
  • products 表中的 BULB2,因为连接的 sku 匹配 fitments 表中的所有三个参数。

标签: mysql


【解决方案1】:
SELECT sku
  FROM fitments 
 WHERE make IN('Ford', 'Chevrolet', 'Honda') 
 GROUP 
    BY sku HAVING COUNT(DISTINCT make) = 3;

【讨论】:

  • +1 表示优雅,但是当许多配件匹配三个中的一个或两个时,这可能会出现性能问题,因为HAVING 可能涉及对磁盘临时表的全表扫描
  • 我认为哪种方法更快(HAVING 或 JOIN)将取决于表的大小而不是数据的稀疏性。
【解决方案2】:

如果你改写你的问题,答案很容易:不要说“加入福特和雪佛兰和本田”,而只需说“加入福特并且加入雪佛兰并且加入本田”:

SELECT
  products.*
  -- , whatever.column you need else
FROM
  products
  INNER JOIN fitments AS fitmentsFord ON products.sku=fitmentsFord.sku
  INNER JOIN fitments AS fitmentsChevrolet ON products.sku=fitmentsChevrolet.sku
  INNER JOIN fitments AS fitmentsHonda ON products.sku=fitmentsHonda.sku
WHERE
  fitmentsFord.make='Ford'
  AND fitmentsChevrolet.make='Chevrolet'
  AND fitmentsHonda.make='Honda'
  AND products.active = 1
  AND fitments.cat_name = 'Cars'
GROUP BY products.sku

【讨论】:

  • 这似乎是我正在寻找的。不过有一个小问题,我绝对不是 JOIN 方面的专家——但如果 fitments 表有大约 30,200 行而 products 表有大约 63,700 行,这是最有效的方法(性能方面)吗?跨度>
  • 询问查询计划!我怀疑连接的高选择性会让这个速度非常快,假设你有必要的索引(*.sku,fitments.make)
  • 请注意,MySQL 允许以这种方式使用 GROUP BY - 通常,它甚至优于 DISTINCT - 但您必须小心避免返回不确定的结果。
  • 因为我怀疑sku 至少接近于唯一(虽然数据库没有强制执行),所以GROUP BY products.sku 符合预期。
【解决方案3】:

试试这个::

SELECT p.sku FROM products p
INNER JOIN 
(Select * from fitments group by make, sku) f ON f.sku = p.sku 
group by p.sku  having count(f.make)=3

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-02
    • 1970-01-01
    • 2015-09-20
    • 1970-01-01
    相关资源
    最近更新 更多