#oracle #snowflake-cloud-data-platform
Вопрос:
Представьте, что у меня есть таблица с некоторым полем, одно из которых является устаревшим массивом. как показано ниже
col1 col2 alldate Max_date
1 2 ["2021-02-12","2021-02-13"] "2021-02-13"
2 3 ["2021-01-12","2021-02-13"] "2021-02-13"
4 4 ["2021-01-12"] "2021-01-12"
5 3 ["2021-01-11","2021-02-12"] "2021-02-12"
6 7 ["2021-02-13"] "2021-02-13"
Мне нужно написать запрос таким образом, чтобы выбрать только тот, у которого в массиве есть максимальная дата. И есть столбец, в котором также указана максимальная дата.
Как должно отображаться в инструкции select
col1 col2 alldate Max_date
1 2 ["2021-02-12","2021-02-13"] "2021-02-13"
2 3 ["2021-01-12","2021-02-13"] "2021-02-13"
6 7 ["2021-02-13"] "2021-02-13"
Таблица огромна, поэтому необходим оптимизированный запрос.
До сих пор я думал о
select col1, col2, maxdate
from t1 where array_contains((select max(max_date) from t1)::variant,date));
Но мне кажется, что запускать инструкцию select для каждого запроса-плохая идея.
Любые Предложения
Комментарии:
1. Привет, каковы типы данных столбцов all_date и max_date?
2. Привет, Дэн — у тебя есть 3 ответа, и некоторые ты поблагодарил лично. Было бы здорово, если бы вы могли проголосовать и принять те, которые вам полезны.
Ответ №1:
Если вам нужна чистая скорость, использование бокового выравнивания на 10% быстрее, чем подход array_contains для более чем 500 000 000 записей на складе XS. Вы можете скопировать приведенный ниже код прямо в snowflake, чтобы проверить его самостоятельно.
Почему боковой плоский подход быстрее? Хорошо, если вы посмотрите на планы запросов, оптимизатор фильтрует на первом шаге (сразу отбраковывает записи), где, поскольку array_contains ожидает до 4-го шага, прежде чем сделать то же самое. Фильтр является классификатором max(max_date) …
Создание Случайного Набора Данных:
create or replace table stack_overflow_68132958 as
SELECT
seq4() col_1,
UNIFORM (1, 500, random()) col_2,
DATEADD(day, UNIFORM (-40, -0, random()), current_date()) random_date_1,
DATEADD(day, UNIFORM (-40, -0, random()), current_date()) random_date_2,
DATEADD(day, UNIFORM (-40, -0, random()), current_date()) random_date_3,
ARRAY_CONSTRUCT(random_date_1, random_date_2, random_date_3) date_array,
greatest(random_date_1, random_date_2, random_date_3) max_date,
to_array(greatest(random_date_1, random_date_2, random_date_3)) max_date_array
FROM
TABLE (GENERATOR (ROWCOUNT => 500000000)) ;
Тестовый подход Фелипе/Майка -> 51 сек
select
distinct
col_1
,col_2
from
stack_overflow_68132958
qualify
array_contains(max(max_date) over () :: variant, date_array);
Тестовый подход Адриана -> 47 секунд
select
distinct
col_1
, col_2
from
stack_overflow_68132958
, lateral flatten(input => date_array) g
qualify
max(max_date) over () = g.value;
Комментарии:
1. Спасибо Анне Флендриг за двойную проверку результатов и помощь в решении.
2. Мы вам более чем рады … хорошо быть внимательным 🙂
Ответ №2:
Я бы, скорее всего, использовал CTE для этого, например:
WITH x AS (
SELECT max(max_date) as max_max_date
FROM t1
)
select col1, col2, maxdate
from t1
cross join x
where array_contains(x.max_max_date::variant,alldate);
Я не проверял синтаксис точно, и типы данных могут немного отличаться, но концепция здесь заключается в том, что CTE будет ОЧЕНЬ быстрым и вернет одну запись с одним значением. Функция MAX() использует метаданные в Snowflake, поэтому она даже не будет использовать хранилище для их получения.
Тем не менее, профилировщик Snowflake довольно умен, поэтому ваш запрос может фактически создать тот же профиль запроса, что и этот оператор. Проверьте их обоих и посмотрите, как выглядит профиль, чтобы понять, действительно ли это имеет значение.
Комментарии:
1. Для развлечения: я добавил протестированную альтернативу (типы стали странными) и решение с одной строкой в qualify
Ответ №3:
Чтобы развить ответ Майка, мы можем сделать все в квалификации, без необходимости в CTE:
with t1 as (
select 'a' col1, 'b' col2, '2020-01-01'::date maxdate, array_construct('2020-01-01'::date, '2018-01-01', '2017-01-01') alldate
)
select col1, col2, alldate, maxdate
from t1
qualify array_contains((max(maxdate) over())::variant, alldate)
;
Обратите внимание, что вы должны быть осторожны с типами. И то и другое верно:
select array_contains('2020-01-01'::date::string::variant, array_construct('2020-01-01', '2019-01-01'));
select array_contains('2020-01-01'::date::variant, array_construct('2020-01-01'::date, '2019-01-01'));
Но это ложь:
select array_contains('2020-01-01'::date::variant, array_construct('2020-01-01', '2019-01-01'));
Ответ №4:
У вас есть несколько замечательных ответов, которые я увидел только после того, как написал свой. Если ваши типы данных совпадают, вы должны быть готовы, скопируйте вставку прямо в snowflake … и это должно сработать.
create or replace schema abc;
use schema abc;
create or replace table myarraytable(col1 number, col2 number, alldates variant, max_date timestamp_ltz);
insert into myarraytable
select 1,2,array_construct('2021-02-12'::timestamp_ltz,'2021-02-13'::timestamp_ltz), '2021-02-13'
union
select 2,3,array_construct('2021-01-12'::timestamp_ltz,'2021-02-13'::timestamp_ltz),'2021-02-13'
union
select 4,4,array_construct('2021-01-12'::timestamp_ltz) , '2021-01-12'
union
select 5,3,array_construct('2021-01-11'::timestamp_ltz,'2021-02-12'::timestamp_ltz) , '2021-02-12'
union
select 6,7,array_construct('2021-02-13'::timestamp_ltz) , '2021-02-13';
select * from myarraytable
order by 1 ;
WITH cte_max AS (
SELECT max(max_date) as max_date
FROM myarraytable
)
select myarraytable.*
from myarraytable, cte_max
where array_contains(cte_max.max_date::variant, alldates)
order by 1 ;