This is just simplified example - actual schema is much more complicated.
Each car is offered in "base" color (offer.model_id = null) or as models in different colors. I need to exclude cars with existing models NOT in BLUE or WHITE but keep cars in base color without extra models.
Schema (PostgreSQL v13)
CREATE TABLE color (
id int4 NOT NULL,
name varchar NOT NULL,
CONSTRAINT color_pkey PRIMARY KEY (id)
);
INSERT INTO color (id, name) VALUES(1, 'WHITE');
INSERT INTO color (id, name) VALUES(2, 'BLUE');
INSERT INTO color (id, name) VALUES(3, 'RED');
INSERT INTO color (id, name) VALUES(4, 'BLACK');
CREATE TABLE car (
id int4 NOT NULL,
name varchar NOT NULL,
CONSTRAINT car_pkey PRIMARY KEY (id)
);
INSERT INTO car (id, name) VALUES(1, 'Ford');
INSERT INTO car (id, name) VALUES(2, 'Skoda');
INSERT INTO car (id, name) VALUES(3, 'Toyota');
CREATE TABLE model (
id int4 NOT NULL,
car_id int4 NOT NULL,
name varchar NOT NULL,
CONSTRAINT model_pkey PRIMARY KEY (id)
);
INSERT INTO model (id, car_id, name) VALUES(1, 1, 'Escort');
INSERT INTO model (id, car_id, name) VALUES(2, 1, 'Puma');
INSERT INTO model (id, car_id, name) VALUES(3, 2, 'Octavia');
INSERT INTO model (id, car_id, name) VALUES(4, 3, 'Yaris');
CREATE TABLE offer (
id int4 NOT NULL,
car_id int4 NOT NULL,
model_id int4,
color_id int4 NOT NULL,
CONSTRAINT offer_pkey PRIMARY KEY (id)
);
ALTER TABLE offer ADD CONSTRAINT "offer_car_id" FOREIGN KEY ("car_id") REFERENCES car(id);
ALTER TABLE offer ADD CONSTRAINT "offer_model_id" FOREIGN KEY ("model_id") REFERENCES model(id);
ALTER TABLE offer ADD CONSTRAINT "offer_color_id" FOREIGN KEY ("color_id") REFERENCES color(id);
CREATE UNIQUE INDEX "offer_car_color" ON offer USING btree (car_id) WHERE (model_id IS NULL);
CREATE UNIQUE INDEX "offer_model_color" ON offer USING btree (car_id, model_id) WHERE (model_id IS NOT NULL);
INSERT INTO offer (id, car_id, model_id, color_id) VALUES(1, 1, null, 1);
INSERT INTO offer (id, car_id, model_id, color_id) VALUES(2, 2, null, 2);
INSERT INTO offer (id, car_id, model_id, color_id) VALUES(3, 3, null, 4);
INSERT INTO offer (id, car_id, model_id, color_id) VALUES(4, 2, 3, 3);
INSERT INTO offer (id, car_id, model_id, color_id) VALUES(5, 3, 4, 2);
Query #1
FULL OFFER - CARS AND MODELS TOGETHER
select car.id, car.name as car_name, model.name as model_name, color.name as offered_color
from offer
left outer join model on model.id = offer.model_id
inner join color on color.id = offer.color_id
inner join car on car.id = offer.car_id
order by car.name, model.name NULLS first;
id | car_name | model_name | offered_color |
---|---|---|---|
1 | Ford | WHITE | |
2 | Skoda | BLUE | |
2 | Skoda | Octavia | RED |
3 | Toyota | BLACK | |
3 | Toyota | Yaris | BLUE |
Query #2
LIST OF CARS IN WHITE OR BLUE (base color or model's color)
select car.id, car.name as car_name, model.name as model_name, color.name as offered_color
from car
inner join offer on offer.car_id = car.id
inner join color on color.id = offer.color_id
left outer join model on model.id = offer.model_id
where color.name in ('WHITE', 'BLUE')
order by car.name;
id | car_name | model_name | offered_color |
---|---|---|---|
1 | Ford | WHITE | |
2 | Skoda | BLUE | |
3 | Toyota | Yaris | BLUE |
Query #3
REQUIRED QUERY: EXCLUDE CARS WITH EXISTING MODELS BUT NOT IN WHITE OR BLUE
select ...?
id | car_name | model_name | offered_color |
---|---|---|---|
1 | Ford | WHITE | |
3 | Toyota | Yaris | BLUE |
Skoda is excluded even it is offered in base BLUE but it has models and non is in BLUE or WHITE.
CodePudding user response:
I think I've found the solution. The question is if it's the best one...?
https://www.db-fiddle.com/f/9Fh6SPxATLuHQCxG7iWktk/6
select
car.id,
car.name as car_name,
model.name as model_name,
color.name as color_name
from
car
inner join offer on
car.id = offer.car_id
inner join color on
color.id = offer.color_id
left join model on
model.id = offer.model_id
where
color.name in ('WHITE', 'BLUE')
and
((not exists (
select
1
from
offer o
where
o.car_id = car.id
and o.model_id is not null))
or (
exists (
select
1
from
offer o
inner join color c on
c.id = o.color_id
where
o.car_id = car.id
and o.model_id is not null
and c.name in ('WHITE', 'BLUE'))
))
order by
car.name;
CodePudding user response:
I can't follow the logic in your description, but I might be able to offer some help on how to write the SQL so you can figure it out.
I suggest adopting the style of using CTEs so you can make it more readable and built it with building blocks.
Once you have all the different conditions you want as CTEs, you can use EXISTS()
to filter based on whether they exist in each of those various conditions.
Example: (this does not produce the correct result, though)
WITH FULL_OFFER AS (
select
car.id,
car.name as car_name,
model.name as model_name,
color.name as offered_color
from offer
left outer join model on model.id = offer.model_id
inner join color on color.id = offer.color_id
inner join car on car.id = offer.car_id
),
BASE_MODELS AS (
SELECT *
FROM FULL_OFFER
WHERE model_name IS NULL
),
BASE_WHITE_OR_BLUE AS (
SELECT *
FROM BASE_MODELS
WHERE COALESCE(offered_color,'NONE') in('WHITE', 'BLUE')
),
NO_MODELS AS (
SELECT *
FROM FULL_OFFER
WHERE NOT EXISTS (
SELECT 1
FROM BASE_MODELS
WHERE FULL_OFFER.id=BASE_MODELS.id
AND FULL_OFFER.offered_color = BASE_MODELS.offered_color)
),
ANY_WHITE_BLUE AS (
SELECT *
FROM FULL_OFFER
WHERE COALESCE(offered_color,'NONE') in('WHITE', 'BLUE')
)
SELECT *
FROM FULL_OFFER
WHERE
(EXISTS (SELECT 1
FROM BASE_WHITE_OR_BLUE
WHERE FULL_OFFER.id = BASE_WHITE_OR_BLUE.id)
AND EXISTS (SELECT 1
FROM NO_MODELS
WHERE FULL_OFFER.id = NO_MODELS.id)
)
OR EXISTS (SELECT 1
FROM ANY_WHITE_BLUE
WHERE FULL_OFFER.id = ANY_WHITE_BLUE.id)
This doesn't produce what you want, but only because I can't translate the conditions from your description.
Hopefully those 2 tips will help you out, if someone else doesn't come along and help you with the correct SQL.