Jason
Jason

Reputation: 13

MySQL SELECT Vehicle fitments selecting and filtering

I'm trying to achieve vehicle part product filtering, where you have products that belong to many different fitments. Such as a particular light bulb can belong to Chevrolet, Ford, and Honda cars.

The problem I'm having is using AND and OR's just aren't working how I need them to.

Here is pseudo code of the results I am looking for (the tables structure and data are at the bottom):

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

Ford, Chevrolet, and Honda don't exist in the same row, so I can't turn this into a WHERE clause?

OR doesn't work because I don't want to return results that don't match every criteria.

AND doesn't work because again, not every row contains all three makes and would return 0 rows.

The result I'm trying to achieve:

The result of the query I need help with would return one products row (not fitments because fitments is just the relations lookup), BULB2 because that matches all three make parameters (Ford, Chevrolet, and Honda) from the possible fitments.

Two tables, Products and Fitments:

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');

Upvotes: 1

Views: 83

Answers (3)

Sashi Kant
Sashi Kant

Reputation: 13465

Try this::

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

Upvotes: 0

Eugen Rieck
Eugen Rieck

Reputation: 65314

The answer comes quite easily, if you rephrase your question: Instead of saying "is joined to Ford AND Chevrolet AND Honda", just say "is joined to Ford AND is joined to Chevrolet AND is joined to Honda":

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

Upvotes: 0

Strawberry
Strawberry

Reputation: 33945

SELECT sku
  FROM fitments 
 WHERE make IN('Ford', 'Chevrolet', 'Honda') 
 GROUP 
    BY sku HAVING COUNT(DISTINCT make) = 3;

Upvotes: 1

Related Questions