Как объединить 3 таблицы в Postgres sql?

Я решал пример SQL Дэнни Ма для Pizza Runner. В одном из разделов «Цены и рейтинги» я застрял в двух вопросах и не могу прийти к правильному решению. Ссылка на кейс: https://8weeksqlchallenge.com/case-study-2/

[cleaned_customer_orders table](https://i.sstatic.net/fEgXb.png)
[cleaned_runner_orders table](https://i.sstatic.net/sl6kd.png)
[extras table](https://i.sstatic.net/Ggm0B.png)
CREATE TABLE customer_orders (  
  "order_id" INTEGER,  
  "customer_id" INTEGER,  
  "pizza_id" INTEGER,    
  "exclusions" VARCHAR(4),  
  "extras" VARCHAR(4),  
  "order_time" TIMESTAMP  
);  

INSERT INTO customer_orders  
  ("order_id", "customer_id", "pizza_id", "exclusions", "extras", "order_time")  
VALUES  
  ('1', '101', '1', '', '', '2020-01-01 18:05:02'),  
  ('2', '101', '1', '', '', '2020-01-01 19:00:52'),  
  ('3', '102', '1', '', '', '2020-01-02 23:51:23'),  
  ('3', '102', '2', '', NULL, '2020-01-02 23:51:23'),  
  ('4', '103', '1', '4', '', '2020-01-04 13:23:46'),  
  ('4', '103', '1', '4', '', '2020-01-04 13:23:46'),  
  ('4', '103', '2', '4', '', '2020-01-04 13:23:46'),  
  ('5', '104', '1', 'null', '1', '2020-01-08 21:00:29'),  
  ('6', '101', '2', 'null', 'null', '2020-01-08 21:03:13'),  
  ('7', '105', '2', 'null', '1', '2020-01-08 21:20:29'),  
  ('8', '102', '1', 'null', 'null', '2020-01-09 23:54:33'),  
  ('9', '103', '1', '4', '1, 5', '2020-01-10 11:22:59'),  
  ('10', '104', '1', 'null', 'null', '2020-01-11 18:34:49'),  
  ('10', '104', '1', '2, 6', '1, 4', '2020-01-11 18:34:49');  


CREATE TABLE runner_orders (
  "order_id" INTEGER,
  "runner_id" INTEGER,
  "pickup_time" VARCHAR(19),
  "distance" VARCHAR(7),
  "duration" VARCHAR(10),
  "cancellation" VARCHAR(23)
);

INSERT INTO runner_orders
  ("order_id", "runner_id", "pickup_time", "distance", "duration", "cancellation")
VALUES
  ('1', '1', '2020-01-01 18:15:34', '20km', '32 minutes', ''),
  ('2', '1', '2020-01-01 19:10:54', '20km', '27 minutes', ''),
  ('3', '1', '2020-01-03 00:12:37', '13.4km', '20 mins', NULL),
  ('4', '2', '2020-01-04 13:53:03', '23.4', '40', NULL),
  ('5', '3', '2020-01-08 21:10:57', '10', '15', NULL),
  ('6', '3', 'null', 'null', 'null', 'Restaurant Cancellation'),
  ('7', '2', '2020-01-08 21:30:45', '25km', '25mins', 'null'),
  ('8', '2', '2020-01-10 00:15:02', '23.4 km', '15 minute', 'null'),
  ('9', '2', 'null', 'null', 'null', 'Customer Cancellation'),
  ('10', '1', '2020-01-11 18:50:20', '10km', '10minutes', 'null');

select order_id, customer_id, pizza_id,
    case
        when exclusions in ('null', '') then null
        else exclusions
    end as exclusions,
    case 
        when extras in ('null', '') then null
        else extras
    end as extras,
    order_time
into cleaned_customer_orders
from customer_orders

select 
    order_id,
    runner_id,
    cast(case 
        when pickup_time = 'null' then null
        else pickup_time
    end as timestamp) as pickup_time,
    cast(case 
        when distance = 'null' then null
        else trim('km' from distance)
    end as float) as distance,
    cast(case
        when duration = 'null' then null
        else substring(duration, 1, 2)
    end as int)as duration,
    case
        when cancellation in ('null', '') then null
        else cancellation
end as cancellation
into cleaned_runner_orders
from runner_orders;

select * from cleaned_customer_orders
select * from cleaned_runner_orders

alter table cleaned_customer_orders
add column record_id serial;

select * from cleaned_customer_orders;

-- to generate extras table
select      
    cco.record_id,
    trim(e.value) as topping_id
into extras
from 
    cleaned_customer_orders as cco
    cross join lateral unnest(string_to_array(cco.extras, ',')) as e(value);

Вопрос 1. Запрос

select   
    sum(case when cco.pizza_id = 1 then 12 else 10 end) as pizza_cost  
from cleaned_customer_orders cco  
join pizza_names pn on pn.pizza_id = cco.pizza_id  
join cleaned_runner_orders cro on cro.order_id = cco.order_id  
where cro.cancellation is null  

В2. Запрос

with cte_extras_cost as (select  
    sum(case when pn.pizza_id = 1 then 12 else 10 end) as pizza_cost,  
    count (e.topping_id)*1 as extras_cost  
    from cleaned_customer_orders cco  
    join pizza_names pn on pn.pizza_id = cco.pizza_id  
    left join extras e on e.record_id = cco.record_id  
    join cleaned_runner_orders cro on cro.order_id = cco.order_id  
    where cro.cancellation is null)  
select pizza_cost, coalesce(extras_cost, 0) as extras_cost, (pizza_cost+coalesce(extras_cost, 0))  
 as total_cost  
from cte_extras_cost;  

Вопрос 5. Запрос

with cte as (  
    select   
    sum(cro.distance*0.30) as delivery_cost,  
    sum(case when cco.pizza_id = 1 then 12 else 10 end) as total_cost  
from cleaned_runner_orders cro  
join cleaned_customer_orders cco on cro.order_id = cco.order_id  
where cro.cancellation is null)  
select cte.total_cost, cte.delivery_cost, (cte.total_cost - cte.delivery_cost) as actual_cost  
from cte  

Мой результат:
Вопрос 1. 138
В2. Pizza_cost=150, extras_cost=4, total_cost=154
Вопрос 5. общая_стоимость=138, стоимость_доставки=64, фактическая_стоимость=73

Правильные ответы:
Вопрос 1. 138
В2. Pizza_cost=138, extras_cost=4, total_cost=142
Вопрос 5. общая_стоимость=138, стоимость_доставки=43, фактическая_стоимость=94

что ты уже испробовал? Где ты застрял? Пожалуйста, поделитесь всеми подробностями в текстовой форме

Nico Haase 15.04.2024 07:46

Выше приведены изображения, на которых я показал, какие таблицы я использую для получения выходных данных и какие запросы я написал. Я получаю результат, но он не подходит для Q2 и Q5, и я не понимаю, почему это происходит?

Minautee 15.04.2024 08:00

Также поделитесь примерами данных и структурой таблицы.

Nico Haase 15.04.2024 08:42

Я не могу напрямую делиться изображениями, так как для этого требуется получить 10 репутаций. Если вы откроете ссылку на таблицы, вы сможете четко увидеть структуру и содержимое таблицы.

Minautee 15.04.2024 08:53

Вам не нужно делиться никакими изображениями. Поделитесь всеми подробностями в текстовой форме, например, утверждениями CREATE TABLE и INSERT.

Nico Haase 15.04.2024 09:29

Я добавил код для таблиц, как они создаются и вставляются значения.

Minautee 15.04.2024 12:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

лучше, если вы опубликуете операторы вставки для заполнения временных таблиц

q1

select   
    sum(case when cco.pizza_id = 1 then 12 else 10 end) as pizza_cost  
from customer_orders cco  
left join runner_orders cro on cro.order_id = cco.order_id  
where coalesce(cro.cancellation,'') not in ('Restaurant Cancellation', 'Customer Cancellation')

кв2

with 
cco as 
(select *, row_number() over(order by order_id, pizza_id) as rn from customer_orders)
, ex as (
select rn, case when extras = 'null' then null else extras end as extras from 
            cco
            where coalesce(case when extras = 'null' then null else extras end,'') <> ''
)
, ex1 as (
select trim(unnest(string_to_array(extras, ',')))::int as topping_id, rn from ex
)
, extras as (
select count(topping_id) as topping_cnt, rn from ex1 group by rn
)
, cte_extras_cost as (
    select 
    sum(case when pn.pizza_id = 1 then 12 else 10 end) pizza_cost
    , sum(topping_cnt*1) as extras_cost
    from cco  
    join pizza_names pn on pn.pizza_id = cco.pizza_id  
    left join extras e on e.rn = cco.rn  
    join runner_orders cro on cro.order_id = cco.order_id  
    where coalesce(cro.cancellation,'') not in ('Restaurant Cancellation', 'Customer Cancellation')
)  
select pizza_cost, coalesce(extras_cost, 0) as extras_cost, (pizza_cost+coalesce(extras_cost, 0))  
 as total_cost  
from cte_extras_cost;

q5

with 
cro as 
(
    select 
        case when distance <> '' then cast(distance as numeric) else 0 end as distance, order_id, runner_id, 
        pickup_time, duration, cancellation 
    from (
    SELECT order_id, runner_id, pickup_time, replace(replace(distance, 'km', ''), 'null', '') as distance, 
    duration, cancellation 
    from 
    pizza_runner.runner_orders
    ) q1
)
, cco as (
select sum(case when pizza_id = 1 then 12 else 10 end) as pizza_cost, order_id
from customer_orders
group by order_id 
)
, cte as (  
    select   
    sum(cro.distance*0.30) as delivery_cost, 
    sum(pizza_cost) as total_cost
from cro  
join cco on cro.order_id = cco.order_id  
where coalesce(cro.cancellation,'') not in ('Restaurant Cancellation', 'Customer Cancellation')
)  
select 
cte.total_cost, cte.delivery_cost, (cte.total_cost - cte.delivery_cost) as actual_cost  
from cte

q1 и q2 имеют четкое решение, НО я не могу найти, как можно получить 45 за стоимость доставки из этих чисел в Q5. счётчик расстояний

да, вы правы, это не 45, а 43, спасибо за решение. Но можете ли вы сказать, что не так с моим кодом?

Minautee 15.04.2024 12:32

q2. Когда вы объединяете заказы и дополнительные услуги, вам нужно использовать группировку и подсчет перед присоединением. В вашем случае вы умножаете стоимость пиццы на количество дополнительных услуг.

AlexTank 15.04.2024 12:44

q5. вам необходимо использовать отношение «один к одному» при объединении заказов, но в заказах клиентов имеется более одной строки на заказ. Вот почему я использую конструкцию с группой по: выберите сумму (случай, когда Pizza_id = 1, затем 12, иначе 10 заканчивается) в качестве Pizza_cost, order_id из группы customer_orders по order_id

AlexTank 15.04.2024 12:48

В шагах перед последним шагом я пытался получить таблицу, подобную вашей, clean_customer_orders и clean_runner_orders, если вы выполняете выбор каждого шага предложения With, вы можете это понять.

AlexTank 15.04.2024 12:52

Другие вопросы по теме