Взвешенная сумма вектора столбца и производного битового вектора - PullRequest
0 голосов
/ 24 июня 2019

У нас есть таблица цен предложения и размеров двух покупателей. Цена предложения p с размером s означает, что покупатель открыт для покупки s количества товара по цене p . У нас есть таблица из четырех столбцов:

  • цена предложения, предложенная двумя покупателями, pA и pB .
  • размеры ставок, сА и сБ .

Наша задача - добавить в таблицу новый столбец с лучшим размером ( bS ), который возвращает размер по лучшей цене. Если два покупателя имеют одинаковую цену, тогда bS равно sA + sB , в противном случае нам нужно принять размер ставки покупателя, который предлагает более высокую цену.

Пример таблицы с желаемым выходом приведен ниже. enter image description here

Простое решение проблемы:

SELECT pA, pB, sA, sB,
  CASE
    WHEN pA = pB THEN sA + sB
    WHEN pA > pB THEN sA
    ELSE sB
  END AS bS
FROM t

Теперь давайте обобщим задачу на четырех покупателей. Стандартное решение SQL -

WITH t_ext AS (
SELECT *, GREATEST(pA, pB, pC, pD) as bestPrice
FROM `t` 
)
SELECT *, (sA * CAST(pA = bestPrice AS INT64) + 
           sB * CAST(pB = bestPrice AS INT64) + 
           sC * CAST(pC = bestPrice AS INT64) +
           sD * CAST(pD = bestPrice AS INT64)) 
AS bS FROM t_ext

Вопрос 1)

Есть ли упрощенный запрос, который

  • использует функцию SUM вместо добавления четырех элементов вручную
  • избежать повторного приведения?

Вопрос 2)

Есть ли способ в экосистеме Google BigQuery повторно использовать этот запрос для другой таблицы с именем столбца, например, цена A , цена B вместо pA , pB ?

Btw. Я написал сообщение в блоге об этой проблеме, посвященное решениям на Python и Q, и мне интересно, как выглядит лучшее решение в стандартном sql.

1 Ответ

2 голосов
/ 24 июня 2019

Ниже приведен стандарт SQL для BigQuery, достаточно общий, чтобы не зависеть от количества покупателей, а также от названия полей цены и размера. Единственное ожидание - сначала идут все цены, а затем все соответствующие размеры, как в вашем примере. Также я предполагаю, что все числа являются целыми числами (как в рассматриваемом примере), но это можно настроить для работы с FLOATs

#standardSQL
WITH t_ext AS (
  SELECT * EXCEPT(arr), 
    ARRAY(SELECT CAST(val AS INT64) FROM UNNEST(arr) val WITH OFFSET WHERE OFFSET < 4) AS prices,
    ARRAY(SELECT CAST(val AS INT64) FROM UNNEST(arr) val WITH OFFSET WHERE OFFSET >= 4) AS sizes,
    (SELECT MAX(CAST(val AS INT64)) FROM UNNEST(arr) val WITH OFFSET WHERE OFFSET < 4) AS bestPrice
  FROM (
    SELECT *, REGEXP_EXTRACT_ALL(TO_JSON_STRING(T), r':(\d+)') AS arr
    FROM `project.dataset.table` t
  )
)
SELECT * EXCEPT(prices, sizes), 
  (SELECT SUM(size)
    FROM UNNEST(prices) price WITH OFFSET
    JOIN UNNEST(sizes) size WITH OFFSET
    USING(OFFSET) 
    WHERE price = bestPrice
  ) AS bS
FROM t_ext  

Единственное, что вам нужно изменить в приведенном выше запросе, это количество покупателей - в приведенных ниже выражениях (в приведенных ниже - 4 можно заменить на ARRAY_LENGTH(arr) / 2

WHERE OFFSET < 4
WHERE OFFSET >= 4
WHERE OFFSET < 4

Например, для данных ниже фиктивных (4 покупателя)

#standardSQL
WITH `project.dataset.table` AS (
  SELECT 1 pA, 2 pB, 3 pC, 4 pD, 1 sA, 1 sB, 1 sC, 5 sD UNION ALL
  SELECT 1, 4, 2, 4, 1, 6, 1, 5 UNION ALL
  SELECT 4, 4, 2, 1, 7, 1, 1, 1
), t_ext AS (
  SELECT * EXCEPT(arr), 
    ARRAY(SELECT CAST(val AS INT64) FROM UNNEST(arr) val WITH OFFSET WHERE OFFSET < 4) AS prices,
    ARRAY(SELECT CAST(val AS INT64) FROM UNNEST(arr) val WITH OFFSET WHERE OFFSET >= 4) AS sizes,
    (SELECT MAX(CAST(val AS INT64)) FROM UNNEST(arr) val WITH OFFSET WHERE OFFSET < 4) AS bestPrice
  FROM (
    SELECT *, REGEXP_EXTRACT_ALL(TO_JSON_STRING(T), r':(\d+)') AS arr
    FROM `project.dataset.table` t
  )
)
SELECT * EXCEPT(prices, sizes), 
  (SELECT SUM(size)
    FROM UNNEST(prices) price WITH OFFSET
    JOIN UNNEST(sizes) size WITH OFFSET
    USING(OFFSET) 
    WHERE price = bestPrice
  ) AS bS
FROM t_ext

результат

Row pA  pB  pC  pD  sA  sB  sC  sD  bestPrice   bS   
1   1   2   3   4   1   1   1   5   4           5    
2   1   4   2   4   1   6   1   5   4           11   
3   4   4   2   1   7   1   1   1   4           8    
...