Reputation: 13
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
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
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
Reputation: 33945
SELECT sku
FROM fitments
WHERE make IN('Ford', 'Chevrolet', 'Honda')
GROUP
BY sku HAVING COUNT(DISTINCT make) = 3;
Upvotes: 1