Важно: Данный раздел актуален для Платформы данных в Публичном облаке и On-Premise.
RT.WideStore может сохранять в ячейках таблиц данные различных типов.
Зависимость имен типов данных от регистра можно проверить в системной таблице system.data_type_families.
Раздел содержит описания поддерживаемых типов данных и специфику их использования и/или реализации, если таковые имеются.
Целые числа фиксированной длины, без знака или со знаком.
При создании таблиц для целых чисел можно указывать числовые параметры (например TINYINT(8)
, SMALLINT(16)
, INT(32)
, BIGINT(64)
), но RT.WideStore их проигнорирует.
Int8
— [-128 : 127]Int16
— [-32768 : 32767]Int32
— [-2147483648 : 2147483647]Int64
— [-9223372036854775808 : 9223372036854775807]Int128
— [-170141183460469231731687303715884105728 : 170141183460469231731687303715884105727]Int256
— [-57896044618658097711785492504343953926634992332820282019728792003956564819968 : 57896044618658097711785492504343953926634992332820282019728792003956564819967]Синонимы:
Int8
— TINYINT
, BOOL
, BOOLEAN
, INT1
.Int16
— SMALLINT
, INT2
.Int32
— INT
, INT4
, INTEGER
.Int64
— BIGINT
.UInt8
— [0 : 255]UInt16
— [0 : 65535]UInt32
— [0 : 4294967295]UInt64
— [0 : 18446744073709551615]UInt128
— [0 : 340282366920938463463374607431768211455]UInt256
— [0 : 115792089237316195423570985008687907853269984665640564039457584007913129639935]Типы эквивалентны типам языка С:
Float32
— float
.Float64
— double
.Синонимы:
Float32
— FLOAT
.Float64
— DOUBLE
.При создании таблиц для чисел с плавающей запятой можно указывать числовые параметры (например, FLOAT(12)
, FLOAT(15, 22)
, DOUBLE(12)
, DOUBLE(4, 18)
), но RT.WideStore их проигнорирует.
Рекомендуется хранить данные в целочисленном виде всегда, когда это возможно. Например, переводите в целочисленные значения числа с фиксированной точностью, такие как денежные суммы или времена загрузки страниц в миллисекундах.
SELECT 1 - 0.9
┌───────minus(1, 0.9)─┐
│ 0.09999999999999998 │
└───────────────────────┘
Inf
) и «не число» (NaN
). Это необходимо учитывать при обработке результатов вычислений.В отличие от стандартного SQL, RT.WideStore поддерживает следующие категории чисел с плавающей запятой:
Inf
— бесконечность.SELECT 0.5 / 0
┌─divide(0.5, 0)─┐
│ inf │
└─────────────────┘
-Inf
— отрицательная бесконечность.SELECT -0.5 / 0
┌─divide(-0.5, 0)─┐
│ -inf │
└─────────────────┘
NaN
— не число.SELECT 0 / 0
┌─divide(0, 0)─┐
│ nan │
└──────────────┘
Смотрите правила сортировки NaN
в разделе Секция ORDER BY .
Знаковые дробные числа с сохранением точности операций сложения, умножения и вычитания. Для деления осуществляется отбрасывание (не округление) знаков, не попадающих в младший десятичный разряд.
В зависимости от параметра P Decimal(P, S) является синонимом:
Например, Decimal32(4) содержит числа от -99999.9999 до 99999.9999 c шагом 0.0001.
Внутри данные представляются как знаковые целые числа, соответсвующей разрядности. Реальные диапазоны, хранящиеся в ячейках памяти несколько больше заявленных. Заявленные диапазоны Decimal проверяются только при вводе числа из строкового представления. Поскольку современные CPU не поддерживают 128-битные числа, операции над Decimal128 эмулируются программно. Decimal128 работает в разы медленней чем Decimal32/Decimal64.
Результат операции между двумя Decimal расширяется до большего типа (независимо от порядка аргументов).
Decimal64(S1) <op> Decimal32(S2) -> Decimal64(S)
Decimal128(S1) <op> Decimal32(S2) -> Decimal128(S)
Decimal128(S1) <op> Decimal64(S2) -> Decimal128(S)
Decimal256(S1) <op> Decimal<32|64|128>(S2) -> Decimal256(S)
Для размера дробной части (scale) результата действуют следующие правила:
При операциях между Decimal и целыми числами результатом является Decimal, аналогичный аргументу.
Операции между Decimal и Float32/64 не определены. Для осуществления таких операций нужно явно привести один из аргументов функциями: toDecimal32, toDecimal64, toDecimal128, или toFloat32, toFloat64. Это сделано из двух соображений. Во-первых, результат операции будет с потерей точности. Во-вторых, преобразование типа - дорогая операция, из-за ее наличия пользовательский запрос может работать в несколько раз дольше.
Часть функций над Decimal возвращают Float64 (например, var, stddev). Для некоторых из них промежуточные операции проходят в Decimal. Для таких функций результат над одинаковыми данными во Float64 и Decimal может отличаться, несмотря на одинаковый тип результата.
При выполнении операций над типом Decimal могут происходить целочисленные переполнения. Лишняя дробная часть отбрасывается (не округляется). Лишняя целочисленная часть приводит к исключению.
SELECT toDecimal32(2, 4) AS x, x / 3
┌──────x─┬─divide(toDecimal32(2, 4), 3)─┐
│ 2.0000 │ 0.6666 │
└─────────┴────────────────────────────────┘
SELECT toDecimal32(4.2, 8) AS x, x * x
DB::Exception: Scale is out of bounds.
SELECT toDecimal32(4.2, 8) AS x, 6 * x
DB::Exception: Decimal math overflow.
Проверка переполнения приводит к замедлению операций. При уверенности, что типа результата хватит для его записи проверку переполнения можно отключить настройкой decimal_check_overflow. В этом случае при переполнении вернется неверное значение:
SET decimal_check_overflow = 0;SELECT toDecimal32(4.2, 8) AS x, 6 * x
┌──────────x──┬─multiply(6, toDecimal32(4.2, 8))─┐
│ 4.20000000 │ -17.74967296 │
└─────────────┴─────────────────────────────────────┘
Переполнения происходят не только на арифметических операциях, но и на операциях сравнения. Отключать проверку стоит только при полной уверенности в корректности результата:
SELECT toDecimal32(1, 8) < 100
DB::Exception: Can't compare.
Смотрите также:
Тип bool
хранится как UInt8. Значения true
(1), false
(0).
select true as col, toTypeName(col);
┌─col──┬─toTypeName(true)─┐
│ true │ Bool │
└───────┴───────────────────┘
select true == 1 as col, toTypeName(col);
┌─col─┬─toTypeName(equals(true, 1))─┐
│ 1 │ UInt8 │
└─────┴────────────────────────────────┘
CREATE TABLE test_bool
(
`A` Int64,
`B` Bool
)
ENGINE = Memory;
INSERT INTO test_bool VALUES (1, true),(2,0);
SELECT * FROM test_bool;
┌─A─┬─B──────┐
│ 1 │ true │
│ 2 │ false │
└───┴────────┘
Строки произвольной длины. Длина не ограничена. Значение может содержать произвольный набор байт, включая нулевые байты. Таким образом, тип String заменяет типы VARCHAR, BLOB, CLOB и т. п. из других СУБД.
При создании таблиц для строк можно указывать числовые параметры (например VARCHAR(255)
), но RT.WideStore их проигнорирует.
В RT.WideStore нет понятия кодировок. Строки могут содержать произвольный набор байт, который хранится и выводится, как есть. Если вам нужно хранить тексты, рекомендуется использовать кодировку UTF-8. По крайней мере, если у вас терминал работает в кодировке UTF-8 (это рекомендуется), вы сможете читать и писать свои значения без каких-либо преобразований. Также, некоторые функции по работе со строками, имеют отдельные варианты, которые работают при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8. Например, функция length вычисляет длину строки в байтах, а функция lengthUTF8 - длину строки в кодовых точках Unicode, при допущении, что значение в кодировке UTF-8.
Строка фиксированной длины N
байт (не символов, не кодовых точек).
Чтобы объявить столбец типа FixedString
, используйте следующий синтаксис:
<column_name> FixedString(N)
Где N
— натуральное число.
Тип FixedString
эффективен, когда данные имеют длину ровно N
байт. Во всех остальных случаях использование FixedString может привести к снижению эффективности.
Примеры значений, которые можно эффективно хранить в столбцах типа FixedString
:
FixedString(16)
для IPv6).FixedString(16)
для MD5, FixedString(32)
для SHA256).Для хранения значений UUID используйте тип данных UUID.
При вставке данных, RT.WideStore:
N
.Too large value for FixedString(N)
, если строка содержит более N
байт.При выборе данных RT.WideStore не обрезает нулевые байты в конце строки. Если вы используете секцию WHERE
, то необходимо добавлять нулевые байты вручную, чтобы RT.WideStore смог сопоставить выражение из фильтра значению FixedString
. Следующий пример показывает, как использовать секцию WHERE
с FixedString
.
Рассмотрим следующую таблицу с единственным столбцом типа FixedString(2)
:
┌─name──┐
│ b │
└───────┘
Запрос SELECT * FROM FixedStringTable WHERE a = 'b'
не возвращает необходимых данных. Необходимо дополнить шаблон фильтра нулевыми байтами.
SELECT * FROM FixedStringTableWHERE a = 'b\0'
┌─a─┐
│ b │
└───┘
Это поведение отличается от поведения MySQL для типа CHAR
, где строки дополняются пробелами, а пробелы перед выводом вырезаются.
Обратите внимание, что длина значения FixedString(N)
постоянна. Функция length возвращает N
даже если значение FixedString(N)
заполнено только нулевыми байтами, однако функция empty в этом же случае возвращает 1
.
Универсальный уникальный идентификатор (UUID) - это 16-байтовое число, используемое для идентификации записей. Подробнее про UUID читайте на Википедии.
Пример UUID значения представлен ниже:
61f0c404-5cb3-11e7-907b-a6006ad3dba0
Если при вставке новой записи значение для UUID-колонки не указано, UUID идентификатор будет заполнен нулями:
00000000-0000-0000-0000-000000000000
Для генерации UUID-значений предназначена функция generateUUIDv4.
Ниже представлены примеры работы с UUID.
Пример 1:
Этот пример демонстрирует, как создать таблицу с UUID-колонкой и добавить в нее сгенерированный UUID.
CREATE TABLE t_uuid (x UUID, y String) ENGINE=TinyLog
INSERT INTO t_uuid SELECT generateUUIDv4(), 'Example 1'
SELECT * FROM t_uuid
┌───────────────────────────────────────x─┬─y──────────┐
│ 417ddc5d-e556-4d27-95dd-a34d84e46a50 │ Example 1 │
└─────────────────────────────────────────┴────────────┘
Пример 2:
В этом примере, при добавлении записи в таблицу значение для UUID-колонки не задано. UUID будет заполнен нулями.
INSERT INTO t_uuid (y) VALUES ('Example 2')
SELECT * FROM t_uuid
┌───────────────────────────────────────x─┬─y──────────┐
│ 417ddc5d-e556-4d27-95dd-a34d84e46a50 │ Example 1 │
│ 00000000-0000-0000-0000-000000000000 │ Example 2 │
└─────────────────────────────────────────┴────────────┘
Тип данных UUID можно использовать только с функциями, которые поддерживаются типом данных String (например, min, max, и count).
Тип данных UUID не поддерживается арифметическими операциями (например, abs) или агрегатными функциями, такими как sum и avg.
Дата. Хранится в двух байтах в виде (беззнакового) числа дней, прошедших от 1970-01-01. Позволяет хранить значения от чуть больше, чем начала unix-эпохи до верхнего порога, определяющегося константой на этапе компиляции (сейчас - до 2106 года, последний полностью поддерживаемый год - 2105).
Диапазон значений: [1970-01-01, 2149-06-06].
Дата хранится без учёта часового пояса.
Пример:
Создание таблицы и добавление в неё данных:
CREATE TABLE dt
(
`timestamp` Date,
`event_id` UInt8
)
ENGINE = TinyLog;
INSERT INTO dt Values (1546300800, 1), ('2019-01-01', 2);
SELECT * FROM dt;
┌──timestamp─┬─event_id─┐
│ 2019-01-01 │ 1 │
│ 2019-01-01 │ 2 │
└─────────────┴───────────┘
См. также:
DateTime
.
Дата. Поддерживается такой же диапазон дат, как для типа DateTime64. Значение хранится в четырех байтах и соответствует числу дней с 1900-01-01 по 2299-12-31.
Пример:
Создание таблицы со столбцом типа Date32
и добавление в нее данных:
CREATE TABLE new
(
`timestamp` Date32,
`event_id` UInt8
)
ENGINE = TinyLog;
INSERT INTO new VALUES (4102444800, 1), ('2100-01-01', 2);
SELECT * FROM new;
┌──timestamp─┬─event_id─┐
│ 2100-01-01 │ 1 │
│ 2100-01-01 │ 2 │
└─────────────┴───────────┘
См. также:
Позволяет хранить момент времени, который может быть представлен как календарная дата и время.
Синтаксис:
DateTime([timezone])
Диапазон значений: [1970-01-01 00:00:00, 2106-02-07 06:28:15].
Точность: 1 секунда.
Момент времени сохраняется как Unix timestamp, независимо от часового пояса и переходов на летнее/зимнее время. Дополнительно, тип DateTime
позволяет хранить часовой пояс, единый для всей колонки, который влияет на то, как будут отображаться значения типа DateTime
в текстовом виде и как будут парситься значения заданные в виде строк (‘2020-01-01 05:00:01’). Часовой пояс не хранится в строках таблицы (выборки), а хранится в метаданных колонки. Список поддерживаемых часовых поясов можно найти в IANA Time Zone Database или получить из базы данных, выполнив запрос SELECT * FROM system.time_zones
. Также список есть в Википедии.
Часовой пояс для столбца типа DateTime
можно в явном виде установить при создании таблицы. Если часовой пояс не установлен, то RT.WideStore использует значение параметра timezone, установленное в конфигурации сервера или в настройках операционной системы на момент запуска сервера.
RT.WideStore отображает значения в зависимости от значения параметра date_time_output_format. Текстовый формат по умолчанию YYYY-MM-DD hh:mm:ss
. Кроме того, вы можете поменять отображение с помощью функции formatDateTime.
При вставке данных в RT.WideStore , можно использовать различные форматы даты и времени в зависимости от значения настройки date_time_input_format.
1. Создание таблицы с столбцом типа DateTime
и вставка данных в неё:
CREATE TABLE dt
(
`timestamp` DateTime('Europe/Moscow'),
`event_id` UInt8
)
ENGINE = TinyLog;
INSERT INTO dt Values (1546300800, 1), ('2019-01-01 00:00:00', 2);
SELECT * FROM dt;
┌────────────timestamp─┬─event_id─┐
│ 2019-01-01 03:00:00 │ 1 │
│ 2019-01-01 00:00:00 │ 2 │
└───────────────────────┴───────────┘
1546300800
в часовом поясе Europe/London (UTC+0)
представляет время '2019-01-01 00:00:00'
. Однако, столбец timestamp
имеет тип DateTime('Europe/Moscow (UTC+3)')
, так что при выводе в виде строки время отобразится как 2019-01-01 03:00:00
.'2019-01-01 00:00:00'
трактуется как время по Москве (и в базу сохраняется 1546290000
)2. Фильтрация по значениям даты-времени
SELECT * FROM dt WHERE timestamp = toDateTime('2019-01-01 00:00:00', 'Europe/Moscow')
┌────────────timestamp─┬─event_id─┐
│ 2019-01-01 00:00:00 │ 2 │
└───────────────────────┴───────────┘
Фильтровать по колонке типа DateTime
можно, указывая строковое значение в фильтре WHERE
. Конвертация будет выполнена автоматически:
SELECT * FROM dt WHERE timestamp = '2019-01-01 00:00:00'
┌────────────timestamp─┬─event_id─┐
│ 2019-01-01 03:00:00 │ 1 │
└───────────────────────┴───────────┘
3. Получение часового пояса для колонки типа DateTime
:
SELECT toDateTime(now(), 'Europe/Moscow') AS column, toTypeName(column) AS x
┌───────────────column─┬─x───────────────────────────┐
│ 2019-10-16 04:12:04 │ DateTime('Europe/Moscow') │
└───────────────────────┴─────────────────────────────┘
4. Конвертация часовых поясов
SELECT
toDateTime(timestamp, 'Europe/London') as lon_time,
toDateTime(timestamp, 'Europe/Moscow') as mos_time
FROM dt
┌────────────lon_time──┬─────────────mos_time─┐
│ 2019-01-01 00:00:00 │ 2019-01-01 03:00:00 │
│ 2018-12-31 21:00:00 │ 2019-01-01 00:00:00 │
└───────────────────────┴───────────────────────┘
Смотри также:
date_time_input_format
,date_time_output_format
,
timezone
,
,
Date
,
DateTime64
.
Позволяет хранить момент времени, который может быть представлен как календарная дата и время, с заданной суб-секундной точностью.
Размер тика (точность, precision): 10-precision секунд, где precision - целочисленный параметр. Возможные значения: [ 0 : 9 ]. Обычно используются - 3 (миллисекунды), 6 (микросекунды), 9 (наносекунды).
Синтаксис:
DateTime64(precision, [timezone])
Данные хранятся в виде количества ‘тиков’, прошедших с момента начала эпохи (1970-01-01 00:00:00 UTC), в Int64. Размер тика определяется параметром precision. Дополнительно, тип DateTime64
позволяет хранить часовой пояс, единый для всей колонки, который влияет на то, как будут отображаться значения типа DateTime64
в текстовом виде и как будут парситься значения заданные в виде строк (‘2020-01-01 05:00:01.000’). Часовой пояс не хранится в строках таблицы (выборки), а хранится в метаданных колонки. Подробнее см. DateTime.
Диапазон значений: [1900-01-01 00:00:00, 2299-12-31 23:59:59.99999999] (Примечание: Точность максимального значения составляет 8).
1. Создание таблицы со столбцом типа DateTime64
и вставка данных в неё:
CREATE TABLE dt
(
`timestamp` DateTime64(3, 'Europe/Moscow'),
`event_id` UInt8
)
ENGINE = TinyLog;
INSERT INTO dt Values (1546300800000, 1), ('2019-01-01 00:00:00', 2);
SELECT * FROM dt;
┌────────────────timestamp─┬─event_id─┐
│ 2019-01-01 03:00:00.000 │ 1 │
│ 2019-01-01 00:00:00.000 │ 2 │
└───────────────────────────┴───────────┘
1546300800
в часовом поясе Europe/London (UTC+0)
представляет время '2019-01-01 00:00:00'
. Однако, столбец timestamp
имеет тип DateTime('Europe/Moscow (UTC+3)')
, так что при выводе в виде строки время отобразится как 2019-01-01 03:00:00
.При вставке даты-времени в виде строки, время трактуется соответственно часовому поясу установленному для колонки. '2019-01-01 00:00:00'
трактуется как время по Москве (и в базу сохраняется '2018-12-31 21:00:00'
в виде Unix Timestamp).
2. Фильтрация по значениям даты и времени
SELECT * FROM dt WHERE timestamp = toDateTime64('2019-01-01 00:00:00', 3, 'Europe/Moscow');
┌────────────────timestamp─┬─event_id─┐
│ 2019-01-01 00:00:00.000 │ 2 │
└───────────────────────────┴───────────┘
В отличие от типа DateTime
, DateTime64
не конвертируется из строк автоматически.
3. Получение часового пояса для значения типа DateTime64
:
SELECT toDateTime64(now(), 3, 'Europe/Moscow') AS column, toTypeName(column) AS x;
┌───────────────────column─┬─x─────────────────────────────────┐
│ 2019-10-16 04:12:04.000 │ DateTime64(3, 'Europe/Moscow') │
└───────────────────────────┴───────────────────────────────────┘
4. Конвертация часовых поясов
SELECT
toDateTime64(timestamp, 3, 'Europe/London') as lon_time,
toDateTime64(timestamp, 3, 'Europe/Moscow') as mos_time
FROM dt;
┌────────────────lon_time──┬──────────────────mos_time─┐
│ 2019-01-01 00:00:00.000 │ 2019-01-01 03:00:00.000 │
│ 2018-12-31 21:00:00.000 │ 2019-01-01 00:00:00.000 │
└───────────────────────────┴───────────────────────────┘
Смотри также:
date_time_input_format
,
date_time_output_format
,
timezone
,
Date
,
DateTime
.
Перечисляемый тип данных, содержащий именованные значения.
Именованные значения задаются либо парами 'string' = integer
, либо именами 'string'
. RT.WideStore хранит только числа, но допускает операции над ними с помощью заданных имён.
RT.WideStore поддерживает:
Enum
. Может содержать до 256 значений, пронумерованных в диапазоне [-128, 127]
.Enum
. Может содержать до 65536 значений, пронумерованных в диапазоне [-32768, 32767]
.RT.WideStore автоматически выбирает размерность Enum
при вставке данных. Чтобы точно понимать размер хранимых данных можно использовать типы Enum8
или Enum16
.
Создадим таблицу со столбцом типа Enum8('hello' = 1, 'world' = 2)
.
CREATE TABLE t_enum
(
x Enum('hello' = 1, 'world' = 2)
)
ENGINE = TinyLog
Номера могут быть опущены - в этом случае RT.WideStore автоматически присвоит последовательные номера, начиная с 1.
CREATE TABLE t_enum
(
x Enum('hello', 'world')
)
ENGINE = TinyLog
Можно также указать допустимый стартовый номер для первого имени.
CREATE TABLE t_enum
(
x Enum('hello' = 1, 'world')
)
ENGINE = TinyLog
CREATE TABLE t_enum
(
x Enum8('hello' = -129, 'world')
)
ENGINE = TinyLog
Exception on server:
Code: 69. DB::Exception: Value -129 for element 'hello' exceeds range of Enum8.
В столбец x
можно сохранять только значения, перечисленные при определении типа, т.е. 'hello'
или 'world'
. Если вы попытаетесь сохранить любое другое значение, RT.WideStore сгенерирует исключение. RT.WideStore автоматически выберет размерность 8-bit для этого Enum
.
INSERT INTO t_enum VALUES ('hello'), ('world'), ('hello')
Ok.
insert into t_enum values('a')
Exception on client:
Code: 49. DB::Exception: Unknown element 'a' for type Enum('hello' = 1, 'world' = 2)
При запросе данных из таблицы RT.WideStore выдаст строковые значения из Enum
.
SELECT * FROM t_enum
┌─x─────┐
│ hello │
│ world │
│ hello │
└───────┘
Если необходимо увидеть цифровые эквиваленты строкам, то необходимо привести тип Enum
к целочисленному.
SELECT CAST(x AS Int8) FROM t_enum
┌─CAST(x, 'Int8')─┐
│ 1 │
│ 2 │
│ 1 │
└──────────────────┘
Чтобы создать значение типа Enum
в запросе, также необходимо использовать функцию CAST
.
SELECT toTypeName(CAST('a', 'Enum(\'a\' = 1, \'b\' = 2)'))
┌─toTypeName(CAST('a', 'Enum(\'a\' = 1, \'b\' = 2)'))─┐
│ Enum8('a' = 1, 'b' = 2) │
└──────────────────────────────────────────────────────────┘
Для каждого из значений прописывается число в диапазоне -128 .. 127
для Enum8
или в диапазоне -32768 .. 32767
для Enum16
. Все строки должны быть разными, числа - тоже. Разрешена пустая строка. При указании такого типа (в определении таблицы), числа могут идти не подряд и в произвольном порядке. При этом, порядок не имеет значения.
Ни строка, ни цифровое значение в Enum
не могут быть NULL.
Enum
может быть передан в тип Nullable. Таким образом, если создать таблицу запросом
CREATE TABLE t_enum_nullable
(
x Nullable( Enum8('hello' = 1, 'world' = 2) )
)
ENGINE = TinyLog
, то в ней можно будет хранить не только 'hello'
и 'world'
, но и NULL
.
INSERT INTO t_enum_nullable Values('hello'),('world'),(NULL)
В оперативке столбец типа Enum
представлен так же, как Int8
или Int16
соответствующими числовыми значениями. При чтении в текстовом виде, парсит значение как строку и ищет соответствующую строку из множества значений Enum-а. Если не находит - кидается исключение. При записи в текстовом виде, записывает значение как соответствующую строку. Если в данных столбца есть мусор - числа не из допустимого множества, то кидается исключение. При чтении и записи в бинарном виде, оно осуществляется так же, как для типов данных Int8, Int16. Неявное значение по умолчанию - это значение с минимальным номером.
При ORDER BY
, GROUP BY
, IN
, DISTINCT
и т. п., Enum-ы ведут себя так же, как соответствующие числа. Например, при ORDER BY они сортируются по числовым значениям. Функции сравнения на равенство и сравнения на отношение порядка двух Enum-ов работают с Enum-ами так же, как с числами.
Сравнивать Enum с числом нельзя. Можно сравнивать Enum с константной строкой - при этом, для строки ищется соответствующее значение Enum-а; если не находится - кидается исключение. Поддерживается оператор IN, где слева стоит Enum, а справа - множество строк. В этом случае, строки рассматриваются как значения соответствующего Enum-а.
Большинство операций с числами и со строками не имеет смысла и не работают для Enum-ов: например, к Enum-у нельзя прибавить число. Для Enum-а естественным образом определяется функция toString
, которая возвращает его строковое значение.
Также для Enum-а определяются функции toT
, где T - числовой тип. При совпадении T с типом столбца Enum-а, преобразование работает бесплатно. При ALTER, есть возможность бесплатно изменить тип Enum-а, если меняется только множество значений. При этом, можно добавлять новые значения; можно удалять старые значения (это безопасно только если они ни разу не использовались, так как это не проверяется). В качестве «защиты от дурака», нельзя менять числовые значения у имеющихся строк - в этом случае, кидается исключение.
При ALTER, есть возможность поменять Enum8 на Enum16 и обратно - так же, как можно поменять Int8 на Int16.
Изменяет внутреннее представление других типов данных, превращая их в тип со словарным кодированием.
Синтаксис:
LowCardinality(data_type)
Параметры:
data_type
— String, FixedString, Date, DateTime и числа за исключением типа Decimal. LowCardinality
неэффективен для некоторых типов данных, см. описание настройки allow_suspicious_low_cardinality_types.Описание:
LowCardinality
— это надстройка, изменяющая способ хранения и правила обработки данных. RT.WideStore применяет словарное кодирование в столбцы типа LowCardinality
. Работа с данными, представленными в словарном виде, может значительно увеличивать производительность запросов SELECT для многих приложений.
Эффективность использования типа данных LowCardinality
зависит от разнообразия данных. Если словарь содержит менее 10 000 различных значений, RT.WideStore в основном показывает более высокую эффективность чтения и хранения данных. Если же словарь содержит более 100 000 различных значений, RT.WideStore может работать хуже, чем при использовании обычных типов данных.
При работе со строками использование LowCardinality
вместо Enum обеспечивает большую гибкость в использовании и часто показывает такую же или более высокую эффективность.
Пример:
Создание таблицы со столбцами типа LowCardinality
:
CREATE TABLE lc_t
(
`id` UInt16,
`strings` LowCardinality(String)
)ENGINE = MergeTree()
ORDER BY id
Настройки:
Функции:
Массив из элементов типа T
. T
может любым, в том числе массивом. Таким образом поддерживаются многомерные массивы. Первый элемент массива имеет индекс 1.
Массив можно создать с помощью функции:
array(T)
Также можно использовать квадратные скобки
[]
Пример создания массива:
SELECT array(1, 2) AS x, toTypeName(x)
┌─x─────┬─toTypeName(array(1, 2))─┐
│ [1,2] │ Array(UInt8) │
└────────┴───────────────────────────┘
SELECT [1, 2] AS x, toTypeName(x)
┌─x─────┬─toTypeName([1, 2])─┐
│ [1,2] │ Array(UInt8) │
└────────┴─────────────────────┘
Максимальный размер массива ограничен одним миллионом элементов.
При создании массива «на лету» RT.WideStore автоматически определяет тип аргументов как наиболее узкий тип данных, в котором можно хранить все перечисленные аргументы. Если среди аргументов есть NULL или аргумент типа Nullable, то тип элементов массива — Nullable.
Если RT.WideStore не смог подобрать тип данных, то он сгенерирует исключение. Это произойдёт, например, при попытке создать массив одновременно со строками и числами SELECT array(1, 'a')
.
Примеры автоматического определения типа данных:
SELECT array(1, 2, NULL) AS x, toTypeName(x)
┌─x───────────┬─toTypeName(array(1, 2, NULL))─┐
│ [1,2,NULL] │ Array(Nullable(UInt8)) │
└─────────────┴──────────────────────────────────┘
Если попытаться создать массив из несовместимых типов данных, то RT.WideStore выбросит исключение:
SELECT array(1, 'a')
Received exception from server (version 1.1.54388)
:Code: 386. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception: There is no supertype for types UInt8, String because some of them are String/FixedString and some of them are not.
Узнать размер массива можно с помощью подстолбца size0
без чтения всего столбца. Для многомерных массивов можно использовать подстолбец sizeN-1
, где N
— требуемое измерение.
Пример:
Запрос:
CREATE TABLE t_arr (`arr` Array(Array(Array(UInt32)))) ENGINE = MergeTree ORDER BY tuple();
INSERT INTO t_arr VALUES ([[[12, 13, 0, 1],[12]]]);
SELECT arr.size0, arr.size1, arr.size2 FROM t_arr;
Результат:
┌─arr.size0─┬─arr.size1─┬─arr.size2─┐
│ 1 │ [2] │ [[4,1]] │
└────────────┴────────────┴────────────┘
Агрегатные функции могут обладать определяемым реализацией промежуточным состоянием, которое может быть сериализовано в тип данных, соответствующий AggregateFunction(…), и быть записано в таблицу обычно посредством материализованного представления. Чтобы получить промежуточное состояние, обычно используются агрегатные функции с суффиксом -State
. Чтобы в дальнейшем получить агрегированные данные необходимо использовать те же агрегатные функции с суффиксом -Merge
.
AggregateFunction(name, types_of_arguments…)
— параметрический тип данных.
Параметры:
Имя агрегатной функции.
Для параметрических агрегатных функций указываются также их параметры.
Типы аргументов агрегатной функции.
Пример
CREATE TABLE t
(
column1 AggregateFunction(uniq, UInt64),
column2 AggregateFunction(anyIf, String, UInt8),
column3 AggregateFunction(quantiles(0.5, 0.9), UInt64)
) ENGINE = ...
uniq, anyIf (any+If) и quantiles — агрегатные функции, поддержанные в RT.WideStore.
Вставка данных:
Для вставки данных используйте INSERT SELECT
с агрегатными -State
-функциями.
Примеры функций:
uniqState(UserID)
quantilesState(0.5, 0.9)(SendTiming)
В отличие от соответствующих функций uniq
и quantiles
, -State
-функциями возвращают не готовое значение, а состояние. То есть, значение типа AggregateFunction
.
В запросах SELECT
значения типа AggregateFunction
выводятся во всех форматах, которые поддерживает RT.WideStore, в виде implementation-specific бинарных данных. Если с помощью SELECT
выполнить дамп данных, например, в формат TabSeparated
, то потом этот дамп можно загрузить обратно с помощью запроса INSERT
.
Выборка данных:
При выборке данных из таблицы AggregatingMergeTree
, используйте GROUP BY
и те же агрегатные функции, что и при вставке данных, но с суффиксом -Merge
.
Агрегатная функция с суффиксом -Merge
берёт множество состояний, объединяет их, и возвращает результат полной агрегации данных.
Например, следующие два запроса возвращают один и тот же результат:
SELECT uniq(UserID) FROM table
SELECT uniqMerge(state) FROM (SELECT uniqState(UserID) AS state FROM table GROUP BY RegionID)
Смотрите в описании движка AggregatingMergeTree.
Nested(Name1 Type1, Name2 Type2, …)
Вложенная структура данных - это как будто вложенная таблица. Параметры вложенной структуры данных - имена и типы столбцов, указываются так же, как у запроса CREATE. Каждой строке таблицы может соответствовать произвольное количество строк вложенной структуры данных.
Пример:
CREATE TABLE test.visits
(
CounterID UInt32,
StartDate Date,
Sign Int8,
IsNew UInt8,
VisitID UInt64,
UserID UInt64,
...
Goals Nested
(
ID UInt32,
Serial UInt32,
EventTime DateTime,
Price Int64,
OrderID String,
CurrencyID UInt32
),
...
)
ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), (CounterID, StartDate, intHash32(UserID), VisitID), 8192, Sign)
В этом примере объявлена вложенная структура данных Goals
, содержащая данные о достижении целей. Каждой строке таблицы visits может соответствовать от нуля до произвольного количества достижений целей.
Если настройка flatten_nested установлена в значение 0
(что не является значением по умолчанию), поддерживаются любые уровни вложенности.
В большинстве случаев, при работе с вложенной структурой данных, указываются отдельные её столбцы. Для этого, имена столбцов указываются через точку. Эти столбцы представляют собой массивы соответствующих типов. Все столбцы-массивы одной вложенной структуры данных имеют одинаковые длины.
Пример:
SELECT
Goals.ID,
Goals.EventTime
FROM test.visits
WHERE CounterID = 101500 AND length(Goals.ID) < 5
LIMIT 10
┌─Goals.ID─────────────────────────┬─Goals.EventTime──────────────────────────────────────────────────────────────────────────────────┐
│ [1073752,591325,591325] │ ['2014-03-17 16:38:10','2014-03-17 16:38:48','2014-03-17 16:42:27'] │
│ [1073752] │ ['2014-03-17 00:28:25'] │
│ [1073752] │ ['2014-03-17 10:46:20'] │
│ [1073752,591325,591325,591325] │ ['2014-03-17 13:59:20','2014-03-17 22:17:55','2014-03-17 22:18:07','2014-03-17 22:18:51'] │
│ [] │ [] │
│ [1073752,591325,591325] │ ['2014-03-17 11:37:06','2014-03-17 14:07:47','2014-03-17 14:36:21'] │
│ [] │ [] │
│ [] │ [] │
│ [591325,1073752] │ ['2014-03-17 00:46:05','2014-03-17 00:46:05'] │
│ [1073752,591325,591325,591325] │ ['2014-03-17 13:28:33','2014-03-17 13:30:26','2014-03-17 18:51:21','2014-03-17 18:51:45'] │
└───────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────┘
Проще всего понимать вложенную структуру данных, как набор из нескольких столбцов-массивов одинаковых длин.
Единственное место, где в запросе SELECT можно указать имя целой вложенной структуры данных, а не отдельных столбцов - секция ARRAY JOIN. Подробнее см. раздел «Секция ARRAY JOIN». Пример:
SELECT
Goal.ID,
Goal.EventTime
FROM test.visits
ARRAY JOIN Goals AS Goal
WHERE CounterID = 101500 AND length(Goals.ID) < 5
LIMIT 10
┌─Goal.ID─┬──────Goal.EventTime─┐
│ 1073752 │ 2014-03-17 16:38:10 │
│ 591325 │ 2014-03-17 16:38:48 │
│ 591325 │ 2014-03-17 16:42:27 │
│ 1073752 │ 2014-03-17 00:28:25 │
│ 1073752 │ 2014-03-17 10:46:20 │
│ 1073752 │ 2014-03-17 13:59:20 │
│ 591325 │ 2014-03-17 22:17:55 │
│ 591325 │ 2014-03-17 22:18:07 │
│ 591325 │ 2014-03-17 22:18:51 │
│ 1073752 │ 2014-03-17 11:37:06 │
└──────────┴───────────────────────┘
Вы не можете сделать SELECT целой вложенной структуры данных. Можно лишь явно перечислить отдельные столбцы - её составляющие.
При запросе INSERT, вы должны передать все составляющие столбцы-массивы вложенной структуры данных по отдельности (как если бы это были отдельные столбцы-массивы). При вставке проверяется, что они имеют одинаковые длины.
При запросе DESCRIBE, столбцы вложенной структуры данных перечисляются так же по отдельности.
Работоспособность запроса ALTER для элементов вложенных структур данных, является сильно ограниченной.
Кортеж из элементов любого типа. Элементы кортежа могут быть одного или разных типов.
Кортежи используются для временной группировки столбцов. Столбцы могут группироваться при использовании выражения IN в запросе, а также для указания нескольких формальных параметров лямбда-функций. Подробнее смотрите разделы Операторы IN, Функции высшего порядка.
Кортежи могут быть результатом запроса. В этом случае, в текстовых форматах кроме JSON, значения выводятся в круглых скобках через запятую. В форматах JSON, кортежи выводятся в виде массивов (в квадратных скобках).
Кортеж можно создать с помощью функции
tuple(T1, T2, ...)
Пример создания кортежа:
SELECT tuple(1,'a') AS x, toTypeName(x)
┌─x────────┬─toTypeName(tuple(1, 'a'))─┐
│ (1,'a') │ Tuple(UInt8, String) │
└──────────┴─────────────────────────────┘
При создании кортежа «на лету» RT.WideStore автоматически определяет тип всех аргументов как минимальный из типов, который может сохранить значение аргумента. Если аргумент — NULL, то тип элемента кортежа — Nullable.
Пример автоматического определения типа данных:
SELECT tuple(1,NULL) AS x, toTypeName(x)
┌─x─────────┬─toTypeName(tuple(1, NULL))──────┐
│ (1,NULL) │ Tuple(UInt8, Nullable(Nothing)) │
└───────────┴────────────────────────────────────┘
К элементам кортежа можно обращаться по индексу и по имени:
CREATE TABLE named_tuples (`a` Tuple(s String, i Int64)) ENGINE = Memory;
INSERT INTO named_tuples VALUES (('y', 10)), (('x',-10));
SELECT a.s FROM named_tuples;
SELECT a.2 FROM named_tuples;
Результат:
┌─a.s─┐
│ y │
│ x │
└─────┘
┌─tupleElement(a, 2)─┐
│ 10 │
│ -10 │
└──────────────────────┘
Позволяет работать как со значением типа TypeName
так и с отсутствием этого значения (NULL) в одной и той же переменной, в том числе хранить NULL
в таблицах вместе со значения типа TypeName
. Например, в столбце типа Nullable(Int8)
можно хранить значения типа Int8
, а в тех строках, где значения нет, будет храниться NULL
.
В качестве TypeName
нельзя использовать составные типы данных Array и Tuple. Составные типы данных могут содержать значения типа Nullable
, например Array(Nullable(Int8))
.
Поле типа Nullable
нельзя включать в индексы.
NULL
— значение по умолчанию для типа Nullable
, если в конфигурации сервера RT.WideStore не указано иное.
Для хранения значения типа Nullable
RT.WideStore использует:
NULL
(далее маска).Маска определяет, что лежит в ячейке данных: NULL
или значение.
В случае, когда маска указывает, что в ячейке хранится NULL
, в файле значений хранится значение по умолчанию для типа данных. Т.е. если, например, поле имеет тип Nullable(Int8)
, то ячейка будет хранить значение по умолчанию для Int8
. Эта особенность увеличивает размер хранилища.
Примечание:
Почти всегда использование `Nullable` снижает производительность, учитывайте это при проектировании своих баз.
Найти в столбце значения NULL
можно с помощью подстолбца null
, при этом весь столбец считывать не требуется. Подстолбец содержит 1
, если соответствующее значение равно NULL
, и 0
если не равно.
Пример:
Запрос:
CREATE TABLE nullable (`n` Nullable(UInt32)) ENGINE = MergeTree ORDER BY tuple();
INSERT INTO nullable VALUES (1) (NULL) (2) (NULL);
SELECT n.null FROM nullable;
Результат:
┌─n.null─┐
│ 0 │
│ 1 │
│ 0 │
│ 1 │
└─────────┘
CREATE TABLE t_null(x Int8, y Nullable(Int8)) ENGINE TinyLog
INSERT INTO t_null VALUES (1, NULL), (2, 3)
SELECT x + y from t_null
┌─plus(x, y)─┐
│ ᴺᵁᴸᴸ │
│ 5 │
└─────────────┘
Значения служебных типов данных не могут сохраняться в таблицу и выводиться в качестве результата, а возникают как промежуточный результат выполнения запроса.
Используется для представления лямбда-выражений в функциях высшего порядка.
Используется для представления правой части выражения IN.
Этот тип данных предназначен только для того, чтобы представлять NULL, т.е. отсутствие значения.
Невозможно создать значение типа Nothing
, поэтому он используется там, где значение не подразумевается. Например, NULL
записывается как Nullable(Nothing)
(Nullable — это тип данных, позволяющий хранить NULL
в таблицах). Также тип Nothing
используется для обозначения пустых массивов:
SELECT toTypeName(Array())
┌─toTypeName(array())─┐
│ Array(Nothing) │
└───────────────────────┘
Семейство типов данных, представляющих интервалы дат и времени. Оператор INTERVAL возвращает значения этих типов.
ВНИМАНИЕ:
Нельзя использовать типы данных `Interval` для хранения данных в таблице.
Структура:
Поддержанные типы интервалов:
SECOND
MINUTE
HOUR
DAY
WEEK
MONTH
QUARTER
YEAR
Каждому типу интервала соответствует отдельный тип данных. Например, тип данных IntervalDay
соответствует интервалу DAY
:
SELECT toTypeName(INTERVAL 4 DAY)
┌─toTypeName(toIntervalDay(4))─┐
│ IntervalDay │
└─────────────────────────────────┘
Значения типов Interval
можно использовать в арифметических операциях со значениями типов Date и DateTime. Например, можно добавить 4 дня к текущей дате:
SELECT now() as current_date_time, current_date_time + INTERVAL 4 DAY
┌───current_date_time─┬─plus(now(), toIntervalDay(4))─┐
│ 2019-10-23 10:58:45 │ 2019-10-27 10:58:45 │
└───────────────────────┴──────────────────────────────────┘
Нельзя объединять интервалы различных типов. Нельзя использовать интервалы вида 4 DAY 1 HOUR
. Вместо этого выражайте интервал в единицах меньших или равных минимальной единице интервала, например, интервал «1 день и 1 час» можно выразить как 25 HOUR
или 90000 SECOND
.
Арифметические операции со значениями типов Interval
не доступны, однако можно последовательно добавлять различные интервалы к значениям типов Date
и DateTime
. Например:
SELECT now() AS current_date_time, current_date_time + INTERVAL 4 DAY + INTERVAL 3 HOUR
┌───current_date_time─┬─plus(plus(now(), toIntervalDay(4)), toIntervalHour(3))─┐
│ 2019-10-23 11:16:28 │ 2019-10-27 14:16:28 │
└───────────────────────┴─────────────────────────────────────────────────────────────┘
Следующий запрос приведёт к генерированию исключения:
select now() AS current_date_time, current_date_time + (INTERVAL 4 DAY + INTERVAL 3 HOUR)
Received exception from server (version 19.14.1):
Code: 43. DB::Exception: Received from localhost:9000. DB::Exception: Wrong argument types for function plus: if one argument is Interval, then another must be Date or DateTime..
Смотрите также:
Домены — это типы данных специального назначения, которые добавляют некоторые дополнительные функции поверх существующего базового типа. На данный момент RT.WideStore не поддерживает пользовательские домены.
Вы можете использовать домены везде, где можно использовать соответствующий базовый тип:
SHOW CREATE TABLE
и DESCRIBE TABLE
INSERT INTO domain_table(domain_column) VALUES(...)
SELECT domain_column FROM domain_table
INSERT INTO domain_table FORMAT CSV ...
ALTER TABLE
.IPv4
— это домен, базирующийся на типе данных UInt32
предназначенный для хранения адресов IPv4. Он обеспечивает компактное хранение данных с удобным для человека форматом ввода-вывода, и явно отображаемым типом данных в структуре таблицы.
Применение:
CREATE TABLE hits (url String, from IPv4) ENGINE = MergeTree() ORDER BY url;
DESCRIBE TABLE hits;
┌─name─┬─type───┬─default_type─┬─default_expression─┬─comment──┬─codec_expression─┐
│ url │ String │ │ │ │ │
│ from │ IPv4 │ │ │ │ │
└───────┴────────┴────────────────┴─────────────────────┴──────────┴────────────────────┘
Или вы можете использовать домен IPv4 в качестве ключа:
CREATE TABLE hits (url String, from IPv4) ENGINE = MergeTree() ORDER BY from;
IPv4
поддерживает вставку в виде строк с текстовым представлением IPv4 адреса:
INSERT INTO hits (url, from) VALUES ('https://wikipedia.org', '116.253.40.133')
('https://mail.ru/', '183.247.232.58');
SELECT * FROM hits;
┌─url────────────────────────────────┬───────────from─┐
│ https://wikipedia.org │ 116.253.40.133 │
│ https://mail.ru/ │ 183.247.232.58 │
└────────────────────────────────────┴─────────────────┘
Значения хранятся в компактной бинарной форме:
SELECT toTypeName(from), hex(from) FROM hits LIMIT 1;
┌─toTypeName(from)─┬─hex(from)─┐
│ IPv4 │ B7F7E83A │
└────────────────────┴────────────┘
Значения с доменным типом данных не преобразуются неявно в другие типы данных, кроме UInt32
. Если необходимо преобразовать значение типа IPv4
в строку, то это необходимо делать явно с помощью функции IPv4NumToString()
:
SELECT toTypeName(s), IPv4NumToString(from) AS s FROM hits LIMIT 1;
┌─toTypeName(IPv4NumToString(from))─┬─s───────────────┐
│ String │ 183.247.232.58 │
└──────────────────────────────────────┴──────────────────┘
Или приводить к типу данных UInt32
:
SELECT toTypeName(i), CAST(from AS UInt32) AS i FROM hits LIMIT 1;
┌─toTypeName(CAST(from, 'UInt32'))─┬───────────i─┐
│ UInt32 │ 3086477370 │
└─────────────────────────────────────┴─────────────┘
IPv6
— это домен, базирующийся на типе данных FixedString(16)
, предназначенный для хранения адресов IPv6. Он обеспечивает компактное хранение данных с удобным для человека форматом ввода-вывода, и явно отображаемым типом данных в структуре таблицы.
Применение:
CREATE TABLE hits (url String, from IPv6) ENGINE = MergeTree() ORDER BY url;
DESCRIBE TABLE hits;
┌─name─┬─type───┬─default_type─┬─default_expression─┬─comment──┬─codec_expression─┐
│ url │ String │ │ │ │ │
│ from │ IPv6 │ │ │ │ │
└──────┴─────────┴───────────────┴──────────────────────┴────────────┴──────────────────┘
Или вы можете использовать домен IPv6
в качестве ключа:
CREATE TABLE hits (url String, from IPv6) ENGINE = MergeTree() ORDER BY from;
IPv6
поддерживает вставку в виде строк с текстовым представлением IPv6 адреса:
INSERT INTO hits (url, from) VALUES ('https://wikipedia.org', '2a02:aa08:e000:3100::2')('https://mail.ru/', '2001:44c8:129:2632:33:0:252:2');
SELECT * FROM hits;
┌─url────────────────────────────────┬─from────────────────────────────┐
│ https://mail.ru/ │ 2001:44c8:129:2632:33:0:252:2 │
│ https://wikipedia.org │ 2a02:aa08:e000:3100::2 │
└────────────────────────────────────┴──────────────────────────────────┘
Значения хранятся в компактной бинарной форме:
SELECT toTypeName(from), hex(from) FROM hits LIMIT 1;
┌─toTypeName(from)─┬─hex(from)──────────────────────────┐
│ IPv6 │ 200144C8012926320033000002520002 │
└───────────────────────┴──────────────────────────────────┘
Значения с доменным типом данных не преобразуются неявно в другие типы данных, кроме FixedString(16)
. Если необходимо преобразовать значение типа IPv6
в строку, то это необходимо делать явно с помощью функции IPv6NumToString()
:
SELECT toTypeName(s), IPv6NumToString(from) AS s FROM hits LIMIT 1;
┌─toTypeName(IPv6NumToString(from))─┬─s────────────────────────────────┐
│ String │ 2001:44c8:129:2632:33:0:252:2 │
└──────────────────────────────────────┴──────────────────────────────────┘
Или приводить к типу данных FixedString(16)
:
SELECT toTypeName(i), CAST(from AS FixedString(16)) AS i FROM hits LIMIT 1;
┌─toTypeName(CAST(from, 'FixedString(16)'))─┬─i───────┐
│ FixedString(16) │ ��� │
└───────────────────────────────────────────────┴─────────┘
При создании таблиц вы можете использовать типы данных с названием, состоящим из нескольких слов. Такие названия поддерживаются для лучшей совместимости с SQL.
Поддержка составных типов:
Составные типы |
Обычные типы |
---|---|
DOUBLE PRECISION | Float64 |
CHAR LARGE OBJECT | String |
CHAR VARYING | String |
CHARACTER LARGE OBJECT | String |
CHARACTER VARYING | String |
NCHAR LARGE OBJECT | String |
NCHAR VARYING | String |
NATIONAL CHARACTER LARGE OBJECT | String |
NATIONAL CHARACTER VARYING | String |
NATIONAL CHAR VARYING | String |
NATIONAL CHARACTER | String |
NATIONAL CHAR | String |
BINARY LARGE OBJECT | String |
BINARY VARYING | String |
RT.WideStore поддерживает типы данных для отображения географических объектов — точек (местоположений), территорий и т.п.
ПРЕДУПРЕЖДЕНИЕ:
Сейчас использование типов данных для работы с географическими структурами является экспериментальной возможностью. Чтобы использовать эти типы данных, включите настройку `allow_experimental_geo_types = 1`.
См. также:
Тип Point
(точка) определяется парой координат X и Y и хранится в виде кортежа Tuple(Float64, Float64).
Пример:
Запрос:
SET allow_experimental_geo_types = 1;
CREATE TABLE geo_point (p Point) ENGINE = Memory();
INSERT INTO geo_point VALUES((10, 10));
SELECT p, toTypeName(p) FROM geo_point;
Результат:
┌─p────────┬─toTypeName(p)─┐
│ (10,10) │ Point │
└──────────┴────────────────┘
Тип Ring
описывает простой многоугольник без внутренних областей (дыр) и хранится в виде массива точек: Array(Point).
Пример:
Запрос:
SET allow_experimental_geo_types = 1;
CREATE TABLE geo_ring (r Ring) ENGINE = Memory();
INSERT INTO geo_ring VALUES([(0, 0), (10, 0), (10, 10), (0, 10)]);
SELECT r, toTypeName(r) FROM geo_ring;
Результат:
┌─r────────────────────────────────┬─toTypeName(r)─┐
│ [(0,0),(10,0),(10,10),(0,10)] │ Ring │
└──────────────────────────────────┴────────────────┘
Тип Polygon
описывает многоугольник с внутренними областями (дырами) и хранится в виде массива: Array(Ring). Первый элемент массива описывает внешний многоугольник (контур), а остальные элементы описывают дыры.
Пример:
Запись в этой таблице описывает многоугольник с одной дырой:
SET allow_experimental_geo_types = 1;
CREATE TABLE geo_polygon (pg Polygon) ENGINE = Memory();
INSERT INTO geo_polygon VALUES([[(20, 20), (50, 20), (50, 50), (20, 50)], [(30, 30), (50, 50), (50, 30)]]);
SELECT pg, toTypeName(pg) FROM geo_polygon;
Результат:
┌─pg─────────────────────────────────────────────────────────────────┬─toTypeName(pg)──┐
│ [[(20,20),(50,20),(50,50),(20,50)],[(30,30),(50,50),(50,30)]] │ Polygon │
└─────────────────────────────────────────────────────────────────────┴─────────────────┘
Тип MultiPolygon
описывает элемент, состоящий из нескольких простых многоугольников (полигональную сетку). Он хранится в виде массива многоугольников: Array(Polygon).
Пример:
Запись в этой таблице описывает элемент, состоящий из двух многоугольников — первый без дыр, а второй с одной дырой:
SET allow_experimental_geo_types = 1;
CREATE TABLE geo_multipolygon (mpg MultiPolygon) ENGINE = Memory();
INSERT INTO geo_multipolygon VALUES([[[(0, 0), (10, 0), (10, 10), (0, 10)]], [[(20, 20), (50, 20), (50, 50), (20, 50)],[(30, 30), (50, 50), (50, 30)]]]);
SELECT mpg, toTypeName(mpg) FROM geo_multipolygon;
Результат:
┌─mpg─────────────────────────────────────────────────────────────────────────────────────────────────────┬─toTypeName(mpg)─┐
│ [[[(0,0),(10,0),(10,10),(0,10)]],[[(20,20),(50,20),(50,50),(20,50)],[(30,30),(50,50),(50,30)]]] │ MultiPolygon │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────┘
Тип данных Map(key, value)
хранит пары ключ:значение
.
Параметры:
key — ключ. String, Integer, LowCardinality, FixedString, UUID, Date, DateTime, Date32, Enum.
value
— значение. Любой тип, включая Map и Array.Чтобы получить значение из колонки a Map('key', 'value')
, используйте синтаксис a['key']
. В настоящее время такая подстановка работает по алгоритму с линейной сложностью.
Примеры:
Рассмотрим таблицу:
CREATE TABLE table_map (a Map(String, UInt64)) ENGINE=Memory;
INSERT INTO table_map VALUES ({'key1':1, 'key2':10}), ({'key1':2,'key2':20}), ({'key1':3,'key2':30});
Выборка всех значений ключа key2
:
SELECT a['key2'] FROM table_map;
Результат:
┌─arrayElement(a, 'key2')─┐
│ 10 │
│ 20 │
│ 30 │
└───────────────────────────┘
Если для какого-то ключа key
в колонке с типом Map()
нет значения, запрос возвращает нули для числовых колонок, пустые строки или пустые массивы.
INSERT INTO table_map VALUES ({'key3':100}), ({});
SELECT a['key3'] FROM table_map;
Результат:
┌─arrayElement(a, 'key3')─┐
│ 100 │
│ 0 │
└───────────────────────────┘
┌─arrayElement(a, 'key3')─┐
│ 0 │
│ 0 │
│ 0 │
└───────────────────────────┘
Для оптимизации обработки столбцов Map
в некоторых случаях можно использовать подстолбцы keys
и values
вместо чтения всего столбца.
Пример:
Запрос:
CREATE TABLE t_map (`a` Map(String, UInt64)) ENGINE = Memory;
INSERT INTO t_map VALUES (map('key1', 1, 'key2', 2, 'key3', 3));
SELECT a.keys FROM t_map;
SELECT a.values FROM t_map;
Результат:
┌─a.keys──────────────────┐
│ ['key1','key2','key3'] │
└──────────────────────────┘
┌─a.values─┐
│ [1,2,3] │
└───────────┘
См. также:
Хранит только текущее значение агрегатной функции и не сохраняет ее полное состояние, как это делает AggregateFunction
. Такая оптимизация может быть применена к функциям, которые обладают следующим свойством: результат выполнения функции f
к набору строк S1 UNION ALL S2
может быть получен путем выполнения f
к отдельным частям набора строк, а затем повторного выполнения f
к результатам: f(S1 UNION ALL S2) = f(f(S1) UNION ALL f(S2))
. Это свойство гарантирует, что результатов частичной агрегации достаточно для вычисления комбинированной, поэтому хранить и обрабатывать какие-либо дополнительные данные не требуется.
Чтобы получить промежуточное значение, обычно используются агрегатные функции с суффиксом -SimpleState.
Поддерживаются следующие агрегатные функции:
any,
anyLast,
min,
max,
sum,
sumWithOverflow,
groupBitAnd,
groupBitOr,
groupBitXor,
groupArrayArray,
groupUniqArrayArray,
sumMap,
minMap,
maxMap.
Примечание:
Значения `SimpleAggregateFunction(func, Type)` отображаются и хранятся так же, как и `Type`, поэтому комбинаторы [-Merge] и [-State] не требуются.`SimpleAggregateFunction` имеет лучшую производительность, чем `AggregateFunction` с той же агрегатной функцией.
Параметры:
func
— имя агрегатной функции.
type
— типы аргументов агрегатной функции.
Пример:
CREATE TABLE simple (id UInt64, val SimpleAggregateFunction(sum, Double)) ENGINE=AggregatingMergeTree ORDER BY id;