левое соединение с использованием двух столбцов и нескольких условий в bigquery

# #sql #google-bigquery

Вопрос:

У меня есть 2 таблицы со следующими данными:

Заказы

код тип заказа дата заказа код поставщика
1 od 2021-02-01 1
2 od 2021-05-03 1
3 ВД 2021-02-04 1
4 ВД 2021-07-01 1

Контракты

дата начала контракта дата заключения контракта тип контракта комиссионный центр код поставщика
2021-01-01 2021-02-28 od 20 1
2021-01-01 2021-02-28 ВД 25 1
2021-03-01 2021-04-30 od 30 1
2021-06-01 2021-10-04 od 35 1

Я хочу назначить каждому заказу процент комиссии на основе кода поставщика, даты и типа. Вот желаемый результат:

Желаемый Результат:

код тип заказа дата заказа код поставщика тип контракта комиссионный центр
1 od 2021-02-01 1 od 20
2 od 2021-05-03 1 od 30
3 ВД 2021-02-04 1 ВД 25
4 ВД 2021-07-01 1 od 35

Первый случай-это когда тип заказа совпадает с типом контракта, а дата заказа находится в пределах contract_start_date и contract_end_date. Это относится к заказам 1 и 3. Однако, в случае, если это условие неверно, я хочу получить последний контракт с max contract_start_date до даты заказа, и я хочу выбрать тип контракта OD, если он существует независимо от типа заказа, а если нет, то выберите последний контракт VD (до даты заказа). Единственный способ, который приходит на ум, — это использовать перекрестное соединение, после которого мне потребуется несколько операторов case для достижения этого результата. Однако это было бы очень медленно для моих данных, так как у меня миллионы записей в заказах, а у каждого поставщика множество контрактов.

Вот мой текущий код, который обслуживает заказы 1 и 3, но я не могу понять заказы 2 и 4.

 with contracts as (select date('2021-01-01') as contract_start_date, date('2021-02-28') as contract_end_date, 'OD' as contract_type, 20 as commission_percentage
union all
select '2021-01-01', '2021-02-28', 'VD', 25
union all
select '2021-03-01', '2021-04-30', 'OD', 30
union all 
select '2021-06-01', current_date(), 'OD', 35
)
, orders as (
select 1 as code, 'OD' as order_type, date('2021-02-01') as order_date
union all 
select 2, 'OD', '2021-05-03'
union all
select 3, 'VD', '2021-02-04'
union all
select 4, 'VD', '2021-07-01')

select o.*, c.contract_type, c.commission_percentage
from orders o left join contracts c on o.order_type = c.contract_type and o.order_date between c.contract_start_date and c.contract_end_date
 

Ответ №1:

Возможно, вам захочется использовать поле «отдельно subqueries с cross join «, так как left outer join поле «без равного ключа» в настоящее время не поддерживается в BigQuery.

 with
contracts as (
    select
        date('2021-01-01') as contract_start_date,
        date('2021-02-28') as contract_end_date,
        'OD' as contract_type,
        20 as commission_percentage
    union all select '2021-01-01', '2021-02-28', 'VD', 25
    union all select '2021-03-01', '2021-04-30', 'OD', 30
    union all select '2021-06-01', current_date(), 'OD', 35
),
orders as (
    select 1 as code, 'OD' as order_type, date('2021-02-01') as order_date
    union all select 2, 'OD', '2021-05-03'
    union all select 3, 'VD', '2021-02-04'
    union all select 4, 'VD', '2021-07-01'
),
latest_contracts_OD AS (
    select *
    from contracts
    WHERE contract_type = 'OD'
),
latest_contracts_VD AS (
    select *
    from contracts
    WHERE contract_type = 'VD'
)
select
    o.*,
    IFNULL(
        IFNULL(c.contract_type, lc_od.contract_type),
        lc_vd.contract_type
    ) as contract_type,
    IFNULL(
        IFNULL(c.commission_percentage, lc_od.commission_percentage),
        lc_vd.commission_percentage
    ) as commission_percentage,
from orders o
left join contracts c
    on o.order_type = c.contract_type
    and o.order_date between c.contract_start_date and c.contract_end_date
cross join latest_contracts_OD lc_od
left outer join latest_contracts_VD lc_vd
    on o.order_type = lc_vd.contract_type
    and o.order_date >= lc_vd.contract_start_date
WHERE o.order_date >= lc_od.contract_start_date
QUALIFY ROW_NUMBER() OVER (PARTITION BY code, order_type ORDER BY lc_od.contract_start_date DESC) = 1
order by code
;