Есть ли более быстрый и менее ресурсоемкий запрос для той же цели? - PullRequest
0 голосов
/ 11 января 2019

У меня есть таблица с 50+ миллионами записей с этой структурой:

sku STRING,
sale_net STRING,
sold_amount FLOAT64,
dt DATE,
is_promo BOOL

Мне нужно выбрать из него записи с sku и sale_nets, у которых было is_promo = true хотя бы один раз.

Например, если в таблице только 3 записи:

sku1 sn1 123 01.01.2018 false
sku1 sn1 456 02.01.2018 true
sku2 sn1 321 01.01.2018 false //this sku-sale_net pair don't have is_promo = true records at other dates

По моему запросу должны быть выбраны только первые два.

Я пишу этот запрос:

select * 
from src_tbl tbl1 
where (select count(*) 
       from src_tbl tbl2 
       where tbl1.sku = tbl2.sku 
         and tbl1.sale_net = tbl2.sale_net 
         and is_promo = true) > 0;

Но он не выполняется на больших базах данных из-за чрезмерного использования ресурса:

Resources exceeded during query execution: The query could not be executed in the allotted memory. Peak usage: 105% of limit. Top memory consumer(s): aggregate functions and GROUP BY clauses: 93% other/unattributed: 7%

Можно ли оптимизировать мой запрос и как?

Ответы [ 6 ]

0 голосов
/ 13 января 2019

Объединение должно работать, оно реализовано более эффективно, чем запросы типа WHERE IN:

WITH promo_sku AS (
  SELECT DISTINCT sku, sale_net
  FROM `project.dataset.src_tbl`
  WHERE is_promo = true
)
SELECT * 
FROM src_tbl tbl1 
JOIN promo_sku ON promo_sku.sku = tbl1.sku AND promo_sku.sale_net = tbl1.sale_net
0 голосов
/ 11 января 2019

Ниже для BigQuery Standard SQL

#standardSQL
SELECT * 
FROM `project.dataset.src_tbl`
WHERE (sku, sale_net) IN (
  SELECT DISTINCT AS STRUCT sku, sale_net 
  FROM `project.dataset.src_tbl`
  WHERE is_promo
)
0 голосов
/ 11 января 2019

Как насчет использования оконных функций?

select * 
from (select t.*,
             countif(ispromo) over (partition by sku, sale_net) as num_promos
      from t
     ) t
where num_promos > 0;
0 голосов
/ 11 января 2019

Я не уверен, что эта работа для вас, так как я понимаю, что работа с большими запросами отличается от обычной работы с БД. Но я все равно дам свое предложение.

Сначала попытайтесь выяснить, у какого sku есть промо.

select sku 
from src_tbl
group by sku
having COUNT( case when is_promo then 1 end) > 0

Если это работает, попробуйте использовать частичный результат или сохраните его как временную таблицу

SELECT *
FROM src_tbl
WHERE sku IN (  select sku 
                from src_tbl
                group by sku
                having COUNT( case when is_promo then 1 end) > 0
              )

Разница в том, что вы делаете только полное сканирование таблицы, чтобы найти все sku с промо, а затем еще одно полное сканирование таблицы, чтобы вернуть строки с соответствующим sku. Вместо того, чтобы выполнить полное сканирование каждой строки, чтобы определить, есть ли у этой акции промо или нет.

0 голосов
/ 11 января 2019

В общем случае существующее предложение будет лучше, чем использование count () для этого типа запроса, поскольку это означает, что БД знает, что может перестать работать после нахождения одной подходящей записи, что-то вроде этого может сработать:

 select * 
 from src_tbl tbl1 
 where exists (select 1 
               from src_tbl tbl2 
               where tbl1.sku = tbl2.sku 
                and tbl1.sale_net = tbl2.sale_net 
                and tbl2.is_promo = true);

Если это все еще не работает, вы можете вообще попытаться избежать коррелированного подзапроса, например:

 select * 
 from src_tbl tbl1 
 where tbl1.sku in( (select tbl2.sku 
               from src_tbl tbl2 
               where tbl2.is_promo = true
               group by tbl2.sku ) );
0 голосов
/ 11 января 2019
select * from src_tbl tbl1 
where exists (select * from src_tbl tbl2 
where tbl1.sku = tbl2.sku and 
      tbl1.sale_net = tbl2.sale_net and 
      tbl2.is_promo = true);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...