У меня довольно простой запрос; это выглядит так:

SELECT
order_date,
pickup_date,
DATE_DIFF(pickup_date,order_date, day) order_to_pickup
FROM
`orders.table`

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

Поэтому вместо приведенного выше запроса возвращаем:

+------------+-------------+-----------------+
| order_date | pickup_date | order_to_pickup |
+------------+-------------+-----------------+
| 3/29/19    | 4/3/19      |               5 |
| 3/29/19    | 4/2/19      |               4 |
+------------+-------------+-----------------+

Хочу вернуть:

+------------+-------------+-----------------+
| order_date | pickup_date | order_to_pickup |
+------------+-------------+-----------------+
| 3/29/19    | 4/3/19      |               2 |
| 3/29/19    | 4/2/19      |               3 |
+------------+-------------+-----------------+
4
Karl Marxdown 2 Май 2019 в 19:04

3 ответа

Лучший ответ

Это должно быть упрощенное решение без брутфорса, о котором упоминал @Elliott Brossard:

select
  order_date,
  pickup_date,
  case 
    when date_diff(pickup_date, order_date, week) > 0 
      then date_diff(pickup_date, order_date, day) - (date_diff(pickup_date, order_date, week) * 2)
    else
      date_diff(pickup_date, order_date, day) 
  end
from `orders.table`
2
khan 2 Май 2019 в 18:36

Я думаю, что можно найти разумное решение, если учесть количество недель между двумя датами, но в то же время мы применяем метод грубой силы:

CREATE TEMP FUNCTION BusinessDateDiff(start_date DATE, end_date DATE) AS (
  (SELECT COUNTIF(MOD(EXTRACT(DAYOFWEEK FROM date), 7) > 1)
   FROM UNNEST(GENERATE_DATE_ARRAY(
       start_date, DATE_SUB(end_date, INTERVAL 1 DAY))) AS date)
);

За ваш вклад я получаю:

CREATE TEMP FUNCTION BusinessDateDiff(start_date DATE, end_date DATE) AS (
  (SELECT COUNTIF(MOD(EXTRACT(DAYOFWEEK FROM date), 7) > 1)
   FROM UNNEST(GENERATE_DATE_ARRAY(
       start_date, DATE_SUB(end_date, INTERVAL 1 DAY))) AS date)
);

WITH OrdersTable AS (
  SELECT
    DATE '2019-03-29' AS order_date,
    DATE '2019-04-03' AS pickup_date UNION ALL
  SELECT
    '2019-03-29',
    '2019-04-02'
)
SELECT
  order_date,
  pickup_date,
  BusinessDateDiff(order_date, pickup_date) AS order_to_pickup
FROM OrdersTable
ORDER BY pickup_date
+------------+-------------+-----------------+
| order_date | pickup_date | order_to_pickup |
+------------+-------------+-----------------+
| 2019-03-29 |  2019-04-02 |               2 |
| 2019-03-29 |  2019-04-03 |               3 |
+------------+-------------+-----------------+
3
Elliott Brossard 2 Май 2019 в 16:33

Вот рабочая методика расчета рабочих дней между датами на основе работы в сообществе Looker Discourse здесь. Исходный пример для Redshift, поэтому я адаптировал его для BigQuery ниже.

SELECT
  CAST(-1*(DATE_DIFF(DATE '2019-01-01', DATE '2019-01-31', DAY) - ((FLOOR(DATE_DIFF(DATE '2019-01-01', DATE '2019-01-31', DAY) / 7) * 2) +
        CASE
          WHEN EXTRACT(DAYOFWEEK  FROM  DATE '2019-01-01') - EXTRACT(DAYOFWEEK  FROM  DATE '2019-01-31') IN (1,  2,  3,  4,  5) AND EXTRACT(DAYOFWEEK  FROM  DATE '2019-01-31') != 0 THEN 2
          ELSE 0
        END +
        CASE
          WHEN EXTRACT(DAYOFWEEK  FROM  DATE '2019-01-01') != 0 AND EXTRACT(DAYOFWEEK  FROM  DATE '2019-01-31') = 0 THEN 1
          ELSE 0
        END +
        CASE
          WHEN EXTRACT(DAYOFWEEK  FROM  DATE '2019-01-01') = 0 AND EXTRACT(DAYOFWEEK  FROM  DATE '2019-01-31') != 0 THEN 1
          ELSE 0 END)) AS int64) AS weekdays

Применяя это к вашему набору данных, мы получаем:

SELECT
  order_date,
  pickup_date,
  CAST(-1*(DATE_DIFF(order_date, pickup_date, DAY) - ((FLOOR(DATE_DIFF(order_date, pickup_date, DAY) / 7) * 2) +
        CASE
          WHEN EXTRACT(DAYOFWEEK  FROM  order_date) - EXTRACT(DAYOFWEEK  FROM  pickup_date) IN (1,  2,  3,  4,  5) AND EXTRACT(DAYOFWEEK  FROM  pickup_date) != 0 THEN 2
          ELSE 0
        END +
        CASE
          WHEN EXTRACT(DAYOFWEEK  FROM  order_date') != 0 AND EXTRACT(DAYOFWEEK  FROM  pickup_date) = 0 THEN 1
            ELSE 0
          END +
          CASE
            WHEN EXTRACT(DAYOFWEEK  FROM  order_date) = 0 AND EXTRACT(DAYOFWEEK  FROM  pickup_date) != 0 THEN 1
            ELSE 0 END)) AS int64) AS weekdays
  FROM
    `orders.table`
1
Eric Keen 2 Май 2019 в 16:58