Как Calcite справляется с преобразованием данных?
Я пытаюсь преобразовать дату, которая хранится в виде строки, в дату, например
ГГГГММДД (строка) в ГГГГ-ММ-ДД (дата)
Насколько я знаю, нет функции преобразования, которая проверяет формат ввода и формат вывода, я пробовал ручную логику, например,
CASE
WHEN CHAR_LENGTH(TRIM(some_string_date)) = 8
THEN
CAST(
SUBSTRING(TRIM(some_string_date) FROM 1 FOR 4)
|| '-'
|| SUBSTRING(TRIM(some_string_date) FROM 5 FOR 2)
||'-'
|| SUBSTRING(TRIM(some_string_date) FROM 7 FOR 2)
as DATE)
ELSE
NULL
END
Однако Apache SQL Validator не принимает это, кто-нибудь видит здесь проблему?
2 ответа
Непосредственно отвечая на вопрос, но, возможно, связанные литералы даты объявляются с DATE
ключевое слово, например, вы можете увидеть примеры в тестах в тестах Beam: один, два и в документах Calcite.
Обновить:
Кажется, что кальцит добавляет некоторую косвенность при выполнении CASE
, Приведение строк в даты работает в общем, как и ожидалось. Например, если входные строки имеют схему (INT f_int, VARCHAR f_string)
и даты в 'YYYYMMDD'
(например (1, '2018')
тогда это работает:
SELECT f_int,
CAST(
SUBSTRING(TRIM(f_string) FROM 1 FOR 4)
||'-'
||SUBSTRING(TRIM(f_string) FROM 5 FOR 2)
||'-'
||SUBSTRING(TRIM(f_string) FROM 7 FOR 2) as DATE)
FROM PCOLLECTION
Даже прямо кастинг 'YYYYMMDD'
работает:
SELECT f_int,
CAST(f_string AS DATE)
FROM PCOLLECTION
Вы можете увидеть все поддерживаемые форматы даты здесь.
Но как только вы заверните это 'CASE ... ELSE NULL'
, то Beam/Calcite, кажется, делают вывод, что тип выражения теперь 'String'
, Это означает, что 'THEN CAST(... AS DATE)'
успешно и возвращает "Дата", но затем он преобразуется в 'String'
когда завернутый в 'CASE'
, Затем, возвращая результат в моем тесте, он, кажется, пытается вернуть его к 'Date'
, но формат строки сейчас не 'YYYYMMDD'
но какой-то другой формат по умолчанию. К сожалению, этого формата нет в списке поддерживаемых, поэтому он не работает.
Временное решение:
Как только вы измените 'ELSE NULL'
к чему-то, что известно как 'Date'
например, 'ELSE DATE "2001-01-01"'
тогда это работает снова, поскольку Beam/Calcite, кажется, не идут, хотя 'String'->'Date'->'String'->'Date'
путь и это работает:
SELECT f_int,
CASE WHEN CHAR_LENGTH(TRIM(f_string)) = 8
THEN CAST (
SUBSTRING(TRIM(f_string) FROM 1 FOR 4)
||'-'
||SUBSTRING(TRIM(f_string) FROM 5 FOR 2)
||'-'
||SUBSTRING(TRIM(f_string) FROM 7 FOR 2) AS DATE)
ELSE DATE '2001-01-01'
END
FROM PCOLLECTION
Я подал BEAM-5789, чтобы отследить лучшее решение.
Обновление 2:
Таким образом, в то время как Calcite генерирует план, сообщающий Beam, что делать, именно Beam в данном случае на самом деле приводит / анализирует даты. Прилагаются усилия для использования встроенных реализаций базовых операций Calcite вместо повторной реализации всего в Beam: https://github.com/apache/beam/pull/6417. После того, как этот запрос извлечения объединен, этот CASE ... ELSE NULL
path должен работать автоматически, если я правильно понял (я предполагаю, что этот класс будет использоваться для обработки значений даты / времени). Он по-прежнему будет проходить через строки, вероятно, излишне, но он должен просто работать.
Если есть MYSQL. Вы можете попробовать
SELECT DATE_FORMAT(STR_TO_DATE('20080908', '%Y%m%d'), "%Y-%m-%d");
Для проверки вы можете проверить, может ли строка быть успешно преобразована в дату. иногда. NULL означает неудачу.