ClickHouse/docs/en/query_language/queries.rst
2017-04-27 23:16:22 +03:00

1424 lines
102 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Queries
-------
CREATE DATABASE
~~~~~~~~~~~~~~~
Creates the 'db_name' database.
::
CREATE DATABASE [IF NOT EXISTS] db_name
A database is just a directory for tables.
If "IF NOT EXISTS" is included, the query won't return an error if the database already exists.
CREATE TABLE
~~~~~~~~~~~~
The ``CREATE TABLE`` query can have several forms.
::
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = engine
Creates a table named 'name' in the 'db' database or the current database if 'db' is not set, with the structure specified in brackets and the 'engine' engine. The structure of the table is a list of column descriptions. If indexes are supported by the engine, they are indicated as parameters for the table engine.
A column description is ``name type`` in the simplest case. For example: ``RegionID UInt32``.
Expressions can also be defined for default values (see below).
::
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name AS [db2.]name2 [ENGINE = engine]
Creates a table with the same structure as another table. You can specify a different engine for the table. If the engine is not specified, the same engine will be used as for the 'db2.name2' table.
::
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] [db.]name ENGINE = engine AS SELECT ...
Creates a table with a structure like the result of the ``SELECT`` query, with the 'engine' engine, and fills it with data from SELECT.
In all cases, if IF NOT EXISTS is specified, the query won't return an error if the table already exists. In this case, the query won't do anything.
Default values
"""""""""""""""""""""
The column description can specify an expression for a default value, in one of the following ways:
``DEFAULT expr``, ``MATERIALIZED expr``, ``ALIAS expr``.
Example: ``URLDomain String DEFAULT domain(URL)``.
If an expression for the default value is not defined, the default values will be set to zeros for numbers, empty strings for strings, empty arrays for arrays, and 0000-00-00 for dates or 0000-00-00 00:00:00 for dates with time. NULLs are not supported.
If the default expression is defined, the column type is optional. If there isn't an explicitly defined type, the default expression type is used. Example: ``EventDate DEFAULT toDate(EventTime)`` - the 'Date' type will be used for the 'EventDate' column.
If the data type and default expression are defined explicitly, this expression will be cast to the specified type using type casting functions. Example: ``Hits UInt32 DEFAULT 0`` means the same thing as ``Hits UInt32 DEFAULT toUInt32(0)``.
Default expressions may be defined as an arbitrary expression from table constants and columns. When creating and changing the table structure, it checks that expressions don't contain loops. For INSERT, it checks that expressions are resolvable - that all columns they can be calculated from have been passed.
``DEFAULT expr``
Normal default value. If the INSERT query doesn't specify the corresponding column, it will be filled in by computing the corresponding expression.
``MATERIALIZED expr``
Materialized expression. Such a column can't be specified for INSERT, because it is always calculated.
For an INSERT without a list of columns, these columns are not considered.
In addition, this column is not substituted when using an asterisk in a SELECT query. This is to preserve the invariant that the dump obtained using SELECT * can be inserted back into the table using INSERT without specifying the list of columns.
``ALIAS expr``
Synonym. Such a column isn't stored in the table at all.
Its values can't be inserted in a table, and it is not substituted when using an asterisk in a SELECT query.
It can be used in SELECTs if the alias is expanded during query parsing.
When using the ALTER query to add new columns, old data for these columns is not written. Instead, when reading old data that does not have values for the new columns, expressions are computed on the fly by default. However, if running the expressions requires different columns that are not indicated in the query, these columns will additionally be read, but only for the blocks of data that need it.
If you add a new column to a table but later change its default expression, the values used for old data will change (for data where values were not stored on the disk). Note that when running background merges, data for columns that are missing in one of the merging parts is written to the merged part.
It is not possible to set default values for elements in nested data structures.
Temporary tables
"""""""""""""""""
In all cases, if TEMPORARY is specified, a temporary table will be created. Temporary tables have the following characteristics:
- Temporary tables disappear when the session ends, including if the connection is lost.
- A temporary table is created with the Memory engine. The other table engines are not supported.
- The DB can't be specified for a temporary table. It is created outside of databases.
- If a temporary table has the same name as another one and a query specifies the table name without specifying the DB, the temporary table will be used.
- For distributed query processing, temporary tables used in a query are passed to remote servers.
In most cases, temporary tables are not created manually, but when using external data for a query, or for distributed (GLOBAL) IN. For more information, see the appropriate sections.
CREATE VIEW
~~~~~~~~~~~~
``CREATE [MATERIALIZED] VIEW [IF NOT EXISTS] [db.]name [ENGINE = engine] [POPULATE] AS SELECT ...``
Creates a view. There are two types of views: normal and MATERIALIZED.
Normal views don't store any data, but just perform a read from another table. In other words, a normal view is nothing more than a saved query. When reading from a view, this saved query is used as a subquery in the FROM clause.
As an example, assume you've created a view:
::
CREATE VIEW view AS SELECT ...
and written a query:
::
SELECT a, b, c FROM view
This query is fully equivalent to using the subquery:
::
SELECT a, b, c FROM (SELECT ...)
Materialized views store data transformed by the corresponding SELECT query.
When creating a materialized view, you can specify ENGINE - the table engine for storing data. By default, it uses the same engine as for the table that the SELECT query is made from.
A materialized view is arranged as follows: when inserting data to the table specified in SELECT, part of the inserted data is converted by this SELECT query, and the result is inserted in the view.
If you specify POPULATE, the existing table data is inserted in the view when creating it, as if making a CREATE TABLE ... AS SELECT ... query. Otherwise, the query contains only the data inserted in the table after creating the view. We don't recommend using POPULATE, since data inserted in the table during the view creation will not be inserted in it.
The SELECT query can contain DISTINCT, GROUP BY, ORDER BY, LIMIT ... Note that the corresponding conversions are performed independently on each block of inserted data. For example, if GROUP BY is set, data is aggregated during insertion, but only within a single packet of inserted data. The data won't be further aggregated. The exception is when using an ENGINE that independently performs data aggregation, such as SummingMergeTree.
The execution of ALTER queries on materialized views has not been fully developed, so they might be inconvenient.
Views look the same as normal tables. For example, they are listed in the result of the SHOW TABLES query.
There isn't a separate query for deleting views. To delete a view, use DROP TABLE.
ATTACH
~~~~~~
The query is exactly the same as CREATE, except
- The word ATTACH is used instead of CREATE.
- The query doesn't create data on the disk, but assumes that data is already in the appropriate places, and just adds information about the table to the server.
After executing an ATTACH query, the server will know about the existence of the table.
This query is used when starting the server. The server stores table metadata as files with ATTACH queries, which it simply runs at launch (with the exception of system tables, which are explicitly created on the server).
DROP
~~~~
This query has two types: ``DROP DATABASE`` and ``DROP TABLE``.
::
DROP DATABASE [IF EXISTS] db
Deletes all tables inside the 'db' database, then deletes the 'db' database itself.
If IF EXISTS is specified, it doesn't return an error if the database doesn't exist.
::
DROP TABLE [IF EXISTS] [db.]name
Deletes the table.
If IF EXISTS is specified, it doesn't return an error if the table doesn't exist or the database doesn't exist.
DETACH
~~~~~~
Deletes information about the table from the server. The server stops knowing about the table's existence.
::
DETACH TABLE [IF EXISTS] [db.]name
This does not delete the table's data or metadata. On the next server launch, the server will read the metadata and find out about the table again. Similarly, a "detached" table can be re-attached using the ATTACH query (with the exception of system tables, which do not have metadata stored for them).
There is no DETACH DATABASE query.
RENAME
~~~~~~
Renames one or more tables.
::
RENAME TABLE [db11.]name11 TO [db12.]name12, [db21.]name21 TO [db22.]name22, ...
All tables are renamed under global locking. Renaming tables is a light operation. If you indicated another database after TO, the table will be moved to this database. However, the directories with databases must reside in the same file system (otherwise, an error is returned).
ALTER
~~~~~
The ALTER query is only supported for *MergeTree type tables, as well as for Merge and Distributed types. The query has several variations.
Column manipulations
""""""""""""""""""""""""
Lets you change the table structure.
::
ALTER TABLE [db].name ADD|DROP|MODIFY COLUMN ...
In the query, specify a list of one or more comma-separated actions. Each action is an operation on a column.
The following actions are supported:
::
ADD COLUMN name [type] [default_expr] [AFTER name_after]
Adds a new column to the table with the specified name, type, and default expression (see the section "Default expressions"). If you specify 'AFTER name_after' (the name of another column), the column is added after the specified one in the list of table columns. Otherwise, the column is added to the end of the table. Note that there is no way to add a column to the beginning of a table. For a chain of actions, 'name_after' can be the name of a column that is added in one of the previous actions.
Adding a column just changes the table structure, without performing any actions with data. The data doesn't appear on the disk after ALTER. If the data is missing for a column when reading from the table, it is filled in with default values (by performing the default expression if there is one, or using zeros or empty strings). The column appears on the disk after merging data parts (see MergeTree).
This approach allows us to complete the ALTER query instantly, without increasing the volume of old data.
.. code-block:: sql
DROP COLUMN name
Deletes the column with the name 'name'.
Deletes data from the file system. Since this deletes entire files, the query is completed almost instantly.
.. code-block:: sql
MODIFY COLUMN name [type] [default_expr]
Changes the 'name' column's type to 'type' and/or the default expression to 'default_expr'. When changing the type, values are converted as if the 'toType' function were applied to them.
If only the default expression is changed, the query doesn't do anything complex, and is completed almost instantly.
Changing the column type is the only complex action - it changes the contents of files with data. For large tables, this may take a long time.
There are several stages of execution:
- Preparing temporary (new) files with modified data.
- Renaming old files.
- Renaming the temporary (new) files to the old names.
- Deleting the old files.
Only the first stage takes time. If there is a failure at this stage, the data is not changed.
If there is a failure during one of the successive stages, data can be restored manually. The exception is if the old files were deleted from the file system but the data for the new files did not get written to the disk and was lost.
There is no support for changing the column type in arrays and nested data structures.
The ALTER query lets you create and delete separate elements (columns) in nested data structures, but not whole nested data structures. To add a nested data structure, you can add columns with a name like 'name.nested_name' and the type 'Array(T)'. A nested data structure is equivalent to multiple array columns with a name that has the same prefix before the dot.
There is no support for deleting of columns in the primary key or the sampling key (columns that are in the ENGINE expression). Changing the type of columns in the primary key is allowed only if such change doesn't entail changing the actual data (e.g. adding the value to an Enum or changing the type from DateTime to UInt32 is allowed).
If the ALTER query is not sufficient for making the table changes you need, you can create a new table, copy the data to it using the INSERT SELECT query, then switch the tables using the RENAME query and delete the old table.
The ALTER query blocks all reads and writes for the table. In other words, if a long SELECT is running at the time of the ALTER query, the ALTER query will wait for the SELECT to complete. At the same time, all new queries to the same table will wait while this ALTER is running.
For tables that don't store data themselves (Merge and Distributed), ALTER just changes the table structure, and does not change the structure of subordinate tables. For example, when running ALTER for a Distributed table, you will also need to run ALTER for the tables on all remote servers.
The ALTER query for changing columns is replicated. The instructions are saved in ZooKeeper, then each replica applies them. All ALTER queries are run in the same order. The query waits for the appropriate actions to be completed on the other replicas. However, a query to change columns in a replicated table can be interrupted, and all actions will be performed asynchronously.
Manipulations with partitions and parts
""""""""""""""""""""""""""""""""""
Only works for tables in the MergeTree family. The following operations are available:
* ``DETACH PARTITION`` - Move a partition to the 'detached' directory and forget it.
* ``DROP PARTITION`` - Delete a partition.
* ``ATTACH PART|PARTITION`` - Add a new part or partition from the 'detached' directory to the table.
* ``FREEZE PARTITION`` - Create a backup of a partition.
* ``FETCH PARTITION`` - Download a partition from another server.
Each type of query is covered separately below.
A partition in a table is data for a single calendar month. This is determined by the values of the date key specified in the table engine parameters. Each month's data is stored separately in order to simplify manipulations with this data.
A "part" in the table is part of the data from a single partition, sorted by the primary key.
You can use the ``system.parts`` table to view the set of table parts and partitions:
::
SELECT * FROM system.parts WHERE active
``active`` - Only count active parts. Inactive parts are, for example, source parts remaining after merging to a larger part - these parts are deleted approximately 10 minutes after merging.
Another way to view a set of parts and partitions is to go into the directory with table data.
The directory with data is
/var/lib/clickhouse/data/database/table/,
where /var/lib/clickhouse/ is the path to ClickHouse data, 'database' is the database name, and 'table' is the table name. Example:
::
$ ls -l /var/lib/clickhouse/data/test/visits/
total 48
drwxrwxrwx 2 clickhouse clickhouse 20480 мая 13 02:58 20140317_20140323_2_2_0
drwxrwxrwx 2 clickhouse clickhouse 20480 мая 13 02:58 20140317_20140323_4_4_0
drwxrwxrwx 2 clickhouse clickhouse 4096 мая 13 02:55 detached
-rw-rw-rw- 1 clickhouse clickhouse 2 мая 13 02:58 increment.txt
Here ``20140317_20140323_2_2_0``, ``20140317_20140323_4_4_0`` - are directories of parts.
Let's look at the name of the first part: ``20140317_20140323_2_2_0``.
* ``20140317`` - minimum date of part data
* ``20140323`` - maximum date of part data .. |br| raw:: html
* ``2`` - minimum number of the data block .. |br| raw:: html
* ``2`` - maximum number of the data block .. |br| raw:: html
* ``0`` - part level - depth of the merge tree that formed it
Each part corresponds to a single partition and contains data for a single month.
201403 - The partition name. A partition is a set of parts for a single month.
On an operating server, you can't manually change the set of parts or their data on the file system, since the server won't know about it. For non-replicated tables, you can do this when the server is stopped, but we don't recommended it. For replicated tables, the set of parts can't be changed in any case.
The 'detached' directory contains parts that are not used by the server - detached from the table using the ALTER ... DETACH query. Parts that are damaged are also moved to this directory, instead of deleting them. You can add, delete, or modify the data in the 'detached' directory at any time - the server won't know about this until you make the ALTER TABLE ... ATTACH query.
::
ALTER TABLE [db.]table DETACH PARTITION 'name'
Move all data for partitions named 'name' to the 'detached' directory and forget about them.
The partition name is specified in YYYYMM format. It can be indicated in single quotes or without them.
After the query is executed, you can do whatever you want with the data in the 'detached' directory — delete it from the file system, or just leave it.
The query is replicated - data will be moved to the 'detached' directory and forgotten on all replicas. The query can only be sent to a leader replica. To find out if a replica is a leader, perform SELECT to the 'system.replicas' system table. Alternatively, it is easier to make a query on all replicas, and all except one will throw an exception.
::
ALTER TABLE [db.]table DROP PARTITION 'name'
Similar to the DETACH operation. Deletes data from the table. Data parts will be tagged as inactive and will be completely deleted in approximately 10 minutes. The query is replicated - data will be deleted on all replicas.
::
ALTER TABLE [db.]table ATTACH PARTITION|PART 'name'
Adds data to the table from the 'detached' directory.
It is possible to add data for an entire partition or a separate part. For a part, specify the full name of the part in single quotes.
The query is replicated. Each replica checks whether there is data in the 'detached' directory. If there is data, it checks the integrity, verifies that it matches the data on the server that initiated the query, and then adds it if everything is correct. If not, it downloads data from the query requestor replica, or from another replica where the data has already been added.
So you can put data in the 'detached' directory on one replica, and use the ALTER ... ATTACH query to add it to the table on all replicas.
::
ALTER TABLE [db.]table FREEZE PARTITION 'name'
Creates a local backup of one or multiple partitions. The name can be the full name of the partition (for example, 201403), or its prefix (for example, 2014) - then the backup will be created for all the corresponding partitions.
The query does the following: for a data snapshot at the time of execution, it creates hardlinks to table data in the directory /var/lib/clickhouse/shadow/N/...
/var/lib/clickhouse/ is the working ClickHouse directory from the config.
N is the incremental number of the backup.
``/var/lib/clickhouse/`` - working directory of ClickHouse from config file.
``N`` - incremental number of backup.
The same structure of directories is created inside the backup as inside ``/var/lib/clickhouse/``.
It also performs 'chmod' for all files, forbidding writes to them.
The backup is created almost instantly (but first it waits for current queries to the corresponding table to finish running). At first, the backup doesn't take any space on the disk. As the system works, the backup can take disk space, as data is modified. If the backup is made for old enough data, it won't take space on the disk.
After creating the backup, data from ``/var/lib/clickhouse/shadow/`` can be copied to the remote server and then deleted on the local server. The entire backup process is performed without stopping the server.
The ``ALTER ... FREEZE PARTITION`` query is not replicated. A local backup is only created on the local server.
As an alternative, you can manually copy data from the ``/var/lib/clickhouse/data/database/table directory``. But if you do this while the server is running, race conditions are possible when copying directories with files being added or changed, and the backup may be inconsistent. You can do this if the server isn't running - then the resulting data will be the same as after the ALTER TABLE t FREEZE PARTITION query.
``ALTER TABLE ... FREEZE PARTITION`` only copies data, not table metadata. To make a backup of table metadata, copy the file ``/var/lib/clickhouse/metadata/database/table.sql``
To restore from a backup:
* Use the CREATE query to create the table if it doesn't exist. The query can be taken from an .sql file (replace ATTACH in it with CREATE).
* Copy data from the ``data/database/table/`` directory inside the backup to the ``/var/lib/clickhouse/data/database/table/detached/`` directory.
* Run ``ALTER TABLE ... ATTACH PARTITION YYYYMM``queries where ``YYYYMM`` is the month, for every month.
In this way, data from the backup will be added to the table.
Restoring from a backup doesn't require stopping the server.
Backups and replication
"""""""""""""""""""
Replication provides protection from device failures. If all data disappeared on one of your replicas, follow the instructions in the "Restoration after failure" section to restore it.
For protection from device failures, you must use replication. For more information about replication, see the section "Data replication".
Backups protect against human error (accidentally deleting data, deleting the wrong data or in the wrong cluster, or corrupting data). For high-volume databases, it can be difficult to copy backups to remote servers. In such cases, to protect from human error, you can keep a backup on the same server (it will reside in /var/lib/clickhouse/shadow/).
::
ALTER TABLE [db.]table FETCH PARTITION 'name' FROM 'path-in-zookeeper'
This query only works for replicatable tables.
It downloads the specified partition from the shard that has its ZooKeeper path specified in the FROM clause, then puts it in the 'detached' directory for the specified table.
Although the query is called ALTER TABLE, it does not change the table structure, and does not immediately change the data available in the table.
Data is placed in the 'detached' directory. You can use the ALTER TABLE ... ATTACH query to attach the data.
The path to ZooKeeper is specified in the FROM clause. For example, ``/clickhouse/tables/01-01/visits``.
Before downloading, the system checks that the partition exists and the table structure matches. The most appropriate replica is selected automatically from the healthy replicas.
The ALTER ... FETCH PARTITION query is not replicated. The partition will be downloaded to the 'detached' directory only on the local server. Note that if after this you use the ALTER TABLE ... ATTACH query to add data to the table, the data will be added on all replicas (on one of the replicas it will be added from the 'detached' directory, and on the rest it will be loaded from neighboring replicas).
Synchronicity of ALTER queries
"""""""""""""""""""""""""""
For non-replicatable tables, all ALTER queries are performed synchronously. For replicatable tables, the query just adds instructions for the appropriate actions to ZooKeeper, and the actions themselves are performed as soon as possible. However, the query can wait for these actions to be completed on all the replicas.
For ``ALTER ... ATTACH|DETACH|DROP`` queries, you can use the ``'replication_alter_partitions_sync'`` setting to set up waiting.
Possible values: 0 - do not wait, 1 - wait for own completion (default), 2 - wait for all.
SHOW DATABASES
~~~~~~~~~~~~~~
.. code-block:: sql
SHOW DATABASES [INTO OUTFILE filename] [FORMAT format]
Prints a list of all databases.
This query is identical to the query ``SELECT name FROM system.databases [INTO OUTFILE filename] [FORMAT format]``
See the section "Formats".
SHOW TABLES
~~~~~~~~~~~
.. code-block:: sql
SHOW TABLES [FROM db] [LIKE 'pattern'] [INTO OUTFILE filename] [FORMAT format]
Outputs a list of
* tables from the current database, or from the 'db' database if "FROM db" is specified.
* all tables, or tables whose name matches the pattern, if "LIKE 'pattern'" is specified.
The query is identical to the query SELECT name FROM system.tables
WHERE database = 'db' [AND name LIKE 'pattern'] [INTO OUTFILE filename] [FORMAT format]
See the section "LIKE operator".
SHOW PROCESSLIST
~~~~~~~~~~~~~~~~
.. code-block:: sql
SHOW PROCESSLIST [INTO OUTFILE filename] [FORMAT format]
Outputs a list of queries currently being processed, other than SHOW PROCESSLIST queries.
Prints a table containing the columns:
**user** is the user who made the query. Keep in mind that for distributed processing, queries are sent to remote servers under the 'default' user. SHOW PROCESSLIST shows the username for a specific query, not for a query that this query initiated.
**address** is the name of the host that the query was sent from. For distributed processing, on remote servers, this is the name of the query requestor host. To track where a distributed query was originally made from, look at SHOW PROCESSLIST on the query requestor server.
**elapsed** - The execution time, in seconds. Queries are output in order of decreasing execution time.
**rows_read**, **bytes_read** - How many rows and bytes of uncompressed data were read when processing the query. For distributed processing, data is totaled from all the remote servers. This is the data used for restrictions and quotas.
**memory_usage** - Current RAM usage in bytes. See the setting 'max_memory_usage'.
**query** - The query itself. In INSERT queries, the data for insertion is not output.
**query_id** - The query identifier. Non-empty only if it was explicitly defined by the user. For distributed processing, the query ID is not passed to remote servers.
This query is exactly the same as: SELECT * FROM system.processes [INTO OUTFILE filename] [FORMAT format].
Tip (execute in the console):
``watch -n1 "clickhouse-client --query='SHOW PROCESSLIST'"``
SHOW CREATE TABLE
~~~~~~~~~~~~~~~~~
.. code-block:: sql
SHOW CREATE TABLE [db.]table [INTO OUTFILE filename] [FORMAT format]
Returns a single String-type 'statement' column, which contains a single value - the CREATE query used for creating the specified table.
DESCRIBE TABLE
~~~~~~~~~~~~~~
.. code-block:: sql
DESC|DESCRIBE TABLE [db.]table [INTO OUTFILE filename] [FORMAT format]
Returns two String-type columns: 'name' and 'type', which indicate the names and types of columns in the specified table.
Nested data structures are output in "expanded" format. Each column is shown separately, with the name after a dot.
EXISTS
~~~~~~
.. code-block:: sql
EXISTS TABLE [db.]name [INTO OUTFILE filename] [FORMAT format]
Returns a single UInt8-type column, which contains the single value 0 if the table or database doesn't exist, or 1 if the table exists in the specified database.
USE
~~~
.. code-block:: sql
USE db
Lets you set the current database for the session.
The current database is used for searching for tables if the database is not explicitly defined in the query with a dot before the table name.
This query can't be made when using the HTTP protocol, since there is no concept of a session.
SET
~~~
.. code-block:: sql
SET [GLOBAL] param = value
Lets you set the 'param' setting to 'value'. You can also make all the settings from the specified settings profile in a single query. To do this, specify 'profile' as the setting name. For more information, see the section "Settings". The setting is made for the session, or for the server (globally) if GLOBAL is specified.
When making a global setting, the setting is not applied to sessions already running, including the current session. It will only be used for new sessions.
Settings made using SET GLOBAL have a lower priority compared with settings made in the config file in the user profile. In other words, user settings can't be overridden by SET GLOBAL.
When the server is restarted, global settings made using SET GLOBAL are lost.
To make settings that persist after a server restart, you can only use the server's config file. (This can't be done using a SET query.)
OPTIMIZE
~~~~~~~~
.. code-block:: sql
OPTIMIZE TABLE [db.]name [PARTITION partition] [FINAL]
Asks the table engine to do something for optimization.
Supported only by *MergeTree engines, in which this query initializes a non-scheduled merge of data parts.
If ``PARTITION`` is specified, then only specified partition will be optimized.
If ``FINAL`` is specified, then optimization will be performed even if data inside the partition already optimized (i. e. all data is in single part).
INSERT
~~~~~~
This query has several variations.
.. code-block:: sql
INSERT INTO [db.]table [(c1, c2, c3)] VALUES (v11, v12, v13), (v21, v22, v23), ...
Inserts rows with the listed values in the 'table' table.
This query is exactly the same as:
.. code-block:: sql
INSERT INTO [db.]table [(c1, c2, c3)] FORMAT Values (v11, v12, v13), (v21, v22, v23), ...
.. code-block:: sql
INSERT INTO [db.]table [(c1, c2, c3)] FORMAT format ...
Inserts data in any specified format.
The data itself comes after 'format', after all space symbols up to the first line break if there is one and including it, or after all space symbols if there isn't a line break. We recommend writing data starting from the next line (this is important if the data starts with space characters).
Example:
.. code-block:: sql
INSERT INTO t FORMAT TabSeparated
11 Hello, world!
22 Qwerty
For more information about data formats, see the section "Formats". The "Interfaces" section describes how to insert data separately from the query when using the command-line client or the HTTP interface.
The query may optionally specify a list of columns for insertion. In this case, the default values are written to the other columns.
Default values are calculated from DEFAULT expressions specified in table definitions, or, if the DEFAULT is not explicitly defined, zeros and empty strings are used. If the 'strict_insert_default' setting is set to 1, all the columns that do not have explicit DEFAULTS must be specified in the query.
.. code-block:: sql
INSERT INTO [db.]table [(c1, c2, c3)] SELECT ...
Inserts the result of the SELECT query into a table.
The names and data types of the SELECT result must exactly match the table structure that data is inserted into, or the specified list of columns.
To change column names, use synonyms (AS) in the SELECT query.
To change data types, use type conversion functions (see the section "Functions").
None of the data formats allows using expressions as values.
In other words, you can't write INSERT INTO t VALUES (now(), 1 + 1, DEFAULT).
There is no support for other data part modification queries:
UPDATE, DELETE, REPLACE, MERGE, UPSERT, INSERT UPDATE.
However, you can delete old data using ALTER TABLE ... DROP PARTITION.
SELECT
~~~~~~
His Highness, the SELECT query.
.. code-block:: sql
SELECT [DISTINCT] expr_list
[FROM [db.]table | (subquery) | table_function] [FINAL]
[SAMPLE sample_coeff]
[ARRAY JOIN ...]
[GLOBAL] ANY|ALL INNER|LEFT JOIN (subquery)|table USING columns_list
[PREWHERE expr]
[WHERE expr]
[GROUP BY expr_list] [WITH TOTALS]
[HAVING expr]
[ORDER BY expr_list]
[LIMIT [n, ]m]
[UNION ALL ...]
[INTO OUTFILE filename]
[FORMAT format]
All the clauses are optional, except for the required list of expressions immediately after SELECT.
The clauses below are described in almost the same order as in the query execution conveyor.
If the query omits the DISTINCT, GROUP BY, and ORDER BY clauses and the IN and JOIN subqueries, the query will be completely stream processed, using O(1) amount of RAM.
Otherwise, the query may consume too much RAM, if appropriate restrictions are not defined (max_memory_usage, max_rows_to_group_by, max_rows_to_sort, max_rows_in_distinct, max_bytes_in_distinct, max_rows_in_set, max_bytes_in_set, max_rows_in_join, max_bytes_in_join, max_bytes_before_external_sort, max_bytes_before_external_group_by). For more information, see the section "Settings". It is possible to use external sorting (saving temporary tables to a disk) and external aggregation. Merge join is not implemented.
FROM clause
"""""""""""
If the FROM clause is omitted, data will be read from the 'system.one' table.
The 'system.one' table contains exactly one row (this table fulfills the same purpose as the DUAL table found in other DBMSs).
The FROM clause specifies the table to read data from, or a subquery, or a table function; ARRAY JOIN and the regular JOIN may also be included (see below).
Instead of a table, the SELECT subquery may be specified in brackets. In this case, the subquery processing pipeline will be built into the processing pipeline of an external query.
In contrast to standard SQL, a synonym does not need to be specified after a subquery. For compatibility, it is possible to write 'AS name' after a subquery, but the specified name isn't used anywhere.
A table function may be specified instead of a table. For more information, see the section "Table functions".
To execute a query, all the columns listed in the query are extracted from the appropriate table. Any columns not needed for the external query are thrown out of the subqueries.
If a query does not list any columns (for example, SELECT count() FROM t), some column is extracted from the table anyway (the smallest one is preferred), in order to calculate the number of rows.
The FINAL modifier can be used only for a SELECT from a CollapsingMergeTree table. When you specify FINAL, data is selected fully "collapsed". Keep in mind that using FINAL leads to a selection that includes columns related to the primary key, in addition to the columns specified in the SELECT. Additionally, the query will be executed in a single stream, and data will be merged during query execution. This means that when using FINAL, the query is processed more slowly. In most cases, you should avoid using FINAL. For more information, see the section "CollapsingMergeTree engine".
SAMPLE clause
"""""""""""""
The SAMPLE clause allows for approximated query processing.
Approximated query processing is only supported by MergeTree* type tables, and only if the sampling expression was specified during table creation (see the section "MergeTree engine").
SAMPLE has the format ``SAMPLE k``, where 'k' is a decimal number from 0 to 1, or ``SAMPLE n``, where 'n' is a sufficiently large integer.
In the first case, the query will be executed on 'k' percent of data. For example, ``SAMPLE 0.1`` runs the query on 10% of data.
In the second case, the query will be executed on a sample of no more than 'n' rows. For example, ``SAMPLE 10000000`` runs the query on a maximum of 10,000,000 rows.
Example:
.. code-block:: sql
SELECT
Title,
count() * 10 AS PageViews
FROM hits_distributed
SAMPLE 0.1
WHERE
CounterID = 34
AND toDate(EventDate) >= toDate('2013-01-29')
AND toDate(EventDate) <= toDate('2013-02-04')
AND NOT DontCountHits
AND NOT Refresh
AND Title != ''
GROUP BY Title
ORDER BY PageViews DESC LIMIT 1000
In this example, the query is executed on a sample from 0.1 (10%) of data. Values of aggregate functions are not corrected automatically, so to get an approximate result, the value 'count()' is manually multiplied by 10.
When using something like ``SAMPLE 10000000``, there isn't any information about which relative percent of data was processed or what the aggregate functions should be multiplied by, so this method of writing is not always appropriate to the situation.
A sample with a relative coefficient is "consistent": if we look at all possible data that could be in the table, a sample (when using a single sampling expression specified during table creation) with the same coefficient always selects the same subset of possible data. In other words, a sample from different tables on different servers at different times is made the same way.
For example, a sample of user IDs takes rows with the same subset of all the possible user IDs from different tables. This allows using the sample in subqueries in the IN clause, as well as for manually correlating results of different queries with samples.
Секция ARRAY JOIN
"""""""""""""""""
Позволяет выполнить JOIN с массивом или вложенной структурой данных. Смысл похож на функцию arrayJoin, но функциональность более широкая.
``ARRAY JOIN`` - это, по сути, ``INNER JOIN`` с массивом. Пример:
.. code-block:: sql
:) CREATE TABLE arrays_test (s String, arr Array(UInt8)) ENGINE = Memory
CREATE TABLE arrays_test
(
s String,
arr Array(UInt8)
) ENGINE = Memory
Ok.
0 rows in set. Elapsed: 0.001 sec.
:) INSERT INTO arrays_test VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', [])
INSERT INTO arrays_test VALUES
Ok.
3 rows in set. Elapsed: 0.001 sec.
:) SELECT * FROM arrays_test
SELECT *
FROM arrays_test
sarr
Hello [1,2]
World [3,4,5]
Goodbye []
3 rows in set. Elapsed: 0.001 sec.
:) SELECT s, arr FROM arrays_test ARRAY JOIN arr
SELECT s, arr
FROM arrays_test
ARRAY JOIN arr
sarr
Hello 1
Hello 2
World 3
World 4
World 5
5 rows in set. Elapsed: 0.001 sec.
Для массива в секции ARRAY JOIN может быть указан алиас. В этом случае, элемент массива будет доступен под этим алиасом, а сам массив - под исходным именем. Пример:
.. code-block:: sql
:) SELECT s, arr, a FROM arrays_test ARRAY JOIN arr AS a
SELECT s, arr, a
FROM arrays_test
ARRAY JOIN arr AS a
sarra
Hello [1,2] 1
Hello [1,2] 2
World [3,4,5] 3
World [3,4,5] 4
World [3,4,5] 5
5 rows in set. Elapsed: 0.001 sec.
В секции ARRAY JOIN может быть указано несколько массивов одинаковых размеров через запятую. В этом случае, JOIN делается с ними одновременно (прямая сумма, а не прямое произведение). Пример:
.. code-block:: sql
:) SELECT s, arr, a, num, mapped FROM arrays_test ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num, arrayMap(x -> x + 1, arr) AS mapped
SELECT s, arr, a, num, mapped
FROM arrays_test
ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num, arrayMap(lambda(tuple(x), plus(x, 1)), arr) AS mapped
sarranummapped
Hello [1,2] 1 1 2
Hello [1,2] 2 2 3
World [3,4,5] 3 1 4
World [3,4,5] 4 2 5
World [3,4,5] 5 3 6
5 rows in set. Elapsed: 0.002 sec.
:) SELECT s, arr, a, num, arrayEnumerate(arr) FROM arrays_test ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num
SELECT s, arr, a, num, arrayEnumerate(arr)
FROM arrays_test
ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num
sarranumarrayEnumerate(arr)
Hello [1,2] 1 1 [1,2]
Hello [1,2] 2 2 [1,2]
World [3,4,5] 3 1 [1,2,3]
World [3,4,5] 4 2 [1,2,3]
World [3,4,5] 5 3 [1,2,3]
5 rows in set. Elapsed: 0.002 sec.
ARRAY JOIN также работает с вложенными структурами данных. Пример:
.. code-block:: sql
:) CREATE TABLE nested_test (s String, nest Nested(x UInt8, y UInt32)) ENGINE = Memory
CREATE TABLE nested_test
(
s String,
nest Nested(
x UInt8,
y UInt32)
) ENGINE = Memory
Ok.
0 rows in set. Elapsed: 0.006 sec.
:) INSERT INTO nested_test VALUES ('Hello', [1,2], [10,20]), ('World', [3,4,5], [30,40,50]), ('Goodbye', [], [])
INSERT INTO nested_test VALUES
Ok.
3 rows in set. Elapsed: 0.001 sec.
:) SELECT * FROM nested_test
SELECT *
FROM nested_test
snest.xnest.y
Hello [1,2] [10,20]
World [3,4,5] [30,40,50]
Goodbye [] []
3 rows in set. Elapsed: 0.001 sec.
:) SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest
SELECT s, `nest.x`, `nest.y`
FROM nested_test
ARRAY JOIN nest
snest.xnest.y
Hello 1 10
Hello 2 20
World 3 30
World 4 40
World 5 50
5 rows in set. Elapsed: 0.001 sec.
При указании имени вложенной структуры данных в ARRAY JOIN, смысл такой же, как ARRAY JOIN со всеми элементами-массивами, из которых она состоит. Пример:
.. code-block:: sql
:) SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest.x, nest.y
SELECT s, `nest.x`, `nest.y`
FROM nested_test
ARRAY JOIN `nest.x`, `nest.y`
snest.xnest.y
Hello 1 10
Hello 2 20
World 3 30
World 4 40
World 5 50
5 rows in set. Elapsed: 0.001 sec.
Такой вариант тоже имеет смысл:
.. code-block:: sql
:) SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest.x
SELECT s, `nest.x`, `nest.y`
FROM nested_test
ARRAY JOIN `nest.x`
snest.xnest.y
Hello 1 [10,20]
Hello 2 [10,20]
World 3 [30,40,50]
World 4 [30,40,50]
World 5 [30,40,50]
5 rows in set. Elapsed: 0.001 sec.
Алиас для вложенной структуры данных можно использовать, чтобы выбрать как результат JOIN-а, так и исходный массив. Пример:
.. code-block:: sql
:) SELECT s, n.x, n.y, nest.x, nest.y FROM nested_test ARRAY JOIN nest AS n
SELECT s, `n.x`, `n.y`, `nest.x`, `nest.y`
FROM nested_test
ARRAY JOIN nest AS n
sn.xn.ynest.xnest.y
Hello 1 10 [1,2] [10,20]
Hello 2 20 [1,2] [10,20]
World 3 30 [3,4,5] [30,40,50]
World 4 40 [3,4,5] [30,40,50]
World 5 50 [3,4,5] [30,40,50]
5 rows in set. Elapsed: 0.001 sec.
Пример использования функции arrayEnumerate:
.. code-block:: sql
:) SELECT s, n.x, n.y, nest.x, nest.y, num FROM nested_test ARRAY JOIN nest AS n, arrayEnumerate(nest.x) AS num
SELECT s, `n.x`, `n.y`, `nest.x`, `nest.y`, num
FROM nested_test
ARRAY JOIN nest AS n, arrayEnumerate(`nest.x`) AS num
sn.xn.ynest.xnest.ynum
Hello 1 10 [1,2] [10,20] 1
Hello 2 20 [1,2] [10,20] 2
World 3 30 [3,4,5] [30,40,50] 1
World 4 40 [3,4,5] [30,40,50] 2
World 5 50 [3,4,5] [30,40,50] 3
5 rows in set. Elapsed: 0.002 sec.
В запросе может быть указано не более одной секции ARRAY JOIN.
Соответствующее преобразование может выполняться как до секции WHERE/PREWHERE (если его результат нужен в этой секции), так и после выполнения WHERE/PREWHERE (чтобы уменьшить объём вычислений).
Секция JOIN
"""""""""""
Обычный JOIN, не имеет отношения к ARRAY JOIN, который описан выше.
.. code-block:: sql
[GLOBAL] ANY|ALL INNER|LEFT [OUTER] JOIN (subquery)|table USING columns_list
Выполняет соединение с данными из подзапроса. В начале выполнения запроса, выполняется подзапрос, указанный после JOIN, и его результат сохраняется в память. Затем производится чтение из "левой" таблицы, указанной в секции FROM, и во время этого чтения, для каждой прочитанной строчки из "левой" таблицы, из таблицы-результата подзапроса ("правой" таблицы) выбираются строчки, соответствующие условию на совпадение значений столбцов, указанных в USING.
Вместо подзапроса может быть указано имя таблицы. Это эквивалентно подзапросу ``SELECT * FROM table``, кроме особого случая, когда таблица имеет движок Join - подготовленное множество для соединения.
Из подзапроса удаляются все ненужные для JOIN-а столбцы.
JOIN-ы бывают нескольких видов:
``INNER`` или ``LEFT`` - тип:
Если указано INNER, то в результат попадают только строки, для которых найдена соответствующая строка в "правой" таблице.
Если указано LEFT, то для строчек "левой" таблицы, для которых нет соответствующих в "правой" таблице, будут присоединены значения "по умолчанию" - нули, пустые строки. Вместо LEFT может быть написано LEFT OUTER - слово OUTER ни на что не влияет.
``ANY`` или ``ALL`` - строгость:
Если указано ``ANY``, то при наличии в "правой" таблице нескольких соответствующих строк, будет присоединена только первая попавшаяся.
Если указано ``ALL``, то при наличии в "правой" таблице нескольких соответствующих строк, данные будут размножены по количеству этих строк.
Использование ALL соответствует обычной семантике JOIN-а из стандартного SQL.
Использование ANY является более оптимальным. Если известно, что в "правой" таблице есть не более одной подходящей строки, то результаты ANY и ALL совпадают. Обязательно необходимо указать ANY или ALL (ни один из этих вариантов не выбран по умолчанию).
``GLOBAL`` - распределённость:
При использовании обычного JOIN-а, запрос отправляется на удалённые серверы, и на каждом из них выполняются подзапросы для формирования "правой" таблицы, и с этой таблицей выполняется соединение. То есть, "правая" таблица формируется на каждом сервере отдельно.
При использовании ``GLOBAL ... JOIN-а``, сначала, на сервере-инициаторе запроса, выполняется подзапрос для вычисления "правой" таблицы, и затем эта временная таблица передаётся на каждый удалённый сервер, и на них выполняются запросы, с использованием этих переданных временных данных.
Следует быть аккуратным при использовании GLOBAL JOIN-ов. Подробнее читайте в разделе "Распределённые подзапросы" ниже.
Возможны все комбинации JOIN-ов. Например, ``GLOBAL ANY LEFT OUTER JOIN``.
При выполнении JOIN-а отсутствует оптимизация порядка выполнения по отношению к другим стадиям запроса: соединение (поиск в "правой" таблице) выполняется до фильтрации в WHERE, до агрегации. Поэтому, чтобы явно задать порядок вычислений, рекомендуется выполнять JOIN подзапроса с подзапросом.
Пример:
.. code-block:: sql
SELECT
CounterID,
hits,
visits
FROM
(
SELECT
CounterID,
count() AS hits
FROM test.hits
GROUP BY CounterID
) ANY LEFT JOIN
(
SELECT
CounterID,
sum(Sign) AS visits
FROM test.visits
GROUP BY CounterID
) USING CounterID
ORDER BY hits DESC
LIMIT 10
CounterIDhitsvisits
1143050 523264 13665
731962 475698 102716
722545 337212 108187
722889 252197 10547
2237260 196036 9522
23057320 147211 7689
722818 90109 17847
48221 85379 4652
19762435 77807 7026
722884 77492 11056
У подзапросов нет возможности задать имена и нет возможности их использовать для того, чтобы сослаться на столбец из конкретного подзапроса.
Требуется, чтобы столбцы, указанные в USING, назывались одинаково в обоих подзапросах, а остальные столбцы - по-разному. Изменить имена столбцов в подзапросах можно с помощью алиасов (в примере используются алиасы hits и visits).
В секции USING указывается один или несколько столбцов для соединения, что обозначает условие на равенство этих столбцов. Список столбцов задаётся без скобок. Более сложные условия соединения не поддерживаются.
"Правая" таблица (результат подзапроса) располагается в оперативке. Если оперативки не хватает, вы не сможете выполнить JOIN.
В запросе (на одном уровне) можно указать только один JOIN. Чтобы выполнить несколько JOIN-ов, вы можете разместить их в подзапросах.
Каждый раз для выполнения запроса с одинаковым JOIN-ом, подзапрос выполняется заново - результат не кэшируется. Это можно избежать, используя специальный движок таблиц Join, представляющий собой подготовленное множество для соединения, которое всегда находится в оперативке. Подробнее смотрите в разделе "Движки таблиц, Join".
В некоторых случаях, вместо использования JOIN достаточно использовать IN - это более эффективно.
Среди разных типов JOIN-ов, наиболее эффективен ANY LEFT JOIN, затем ANY INNER JOIN; наименее эффективны ALL LEFT JOIN и ALL INNER JOIN.
Если JOIN необходим для соединения с таблицами измерений (dimension tables - сравнительно небольшие таблицы, которые содержат свойства измерений - например, имена для рекламных кампаний), то использование JOIN может быть не очень удобным из-за громоздкости синтаксиса, а также из-за того, что правая таблица читается заново при каждом запросе. Специально для таких случаев существует функциональность "Внешние словари", которую следует использовать вместо JOIN. Подробнее смотрите раздел "Внешние словари".
Секция WHERE
""""""""""""
Секция WHERE, если есть, должна содержать выражение, имеющее тип UInt8. Обычно это какое-либо выражение с операторами сравнения и логическими операторами.
Это выражение будет использовано для фильтрации данных перед всеми остальными преобразованиями.
Выражение анализируется на возможность использования индексов, если индексы поддерживаются движком таблицы.
Секция PREWHERE
"""""""""""""""
Имеет такой же смысл, как и секция WHERE. Отличие состоит в том, какие данные читаются из таблицы.
При использовании PREWHERE, из таблицы сначала читаются только столбцы, необходимые для выполнения PREWHERE. Затем читаются остальные столбцы, нужные для выполнения запроса, но из них только те блоки, в которых выражение в PREWHERE истинное.
PREWHERE имеет смысл использовать, если есть условия фильтрации, не подходящие под индексы, которые использует меньшинство столбцов из тех, что есть в запросе, но достаточно сильно фильтрует данные. Таким образом, сокращается количество читаемых данных.
Например, полезно писать PREWHERE для запросов, которые вынимают много столбцов, но в которых фильтрация производится лишь по нескольким столбцам.
PREWHERE поддерживается только таблицами семейства *MergeTree.
В запросе могут быть одновременно указаны секции PREWHERE и WHERE. В этом случае, PREWHERE идёт перед WHERE.
Следует иметь ввиду, что указывать в PREWHERE только столбцы, по которым существует индекс, имеет мало смысла, так как при использовании индекса и так читаются лишь блоки данных, соответствующие индексу.
Если настройка optimize_move_to_prewhere выставлена в 1, то при отсутствии PREWHERE, система будет автоматически переносить части выражений из WHERE в PREWHERE согласно некоторой эвристике.
Секция GROUP BY
"""""""""""""""
Это одна из наиболее важных частей СУБД.
Секция GROUP BY, если есть, должна содержать список выражений. Каждое выражение далее будем называть "ключом".
При этом, все выражения в секциях SELECT, HAVING, ORDER BY, должны вычисляться из ключей или из агрегатных функций. То есть, каждый выбираемый из таблицы столбец, должен использоваться либо в ключах, либо внутри агрегатных функций.
Если запрос содержит столбцы таблицы только внутри агрегатных функций, то секция GROUP BY может не указываться, и подразумевается агрегация по пустому набору ключей.
Пример:
.. code-block:: sql
SELECT
count(),
median(FetchTiming > 60 ? 60 : FetchTiming),
count() - sum(Refresh)
FROM hits
Но, в отличие от стандартного SQL, если в таблице нет строк (вообще нет или после фильтрации с помощью WHERE), в качестве результата возвращается пустой результат, а не результат из одной строки, содержащий "начальные" значения агрегатных функций.
В отличие от MySQL (и в соответствии со стандартом SQL), вы не можете получить какое-нибудь значение некоторого столбца, не входящего в ключ или агрегатную функцию (за исключением константных выражений). Для обхода этого вы можете воспользоваться агрегатной функцией any (получить первое попавшееся значение) или min/max.
Пример:
.. code-block:: sql
SELECT
domainWithoutWWW(URL) AS domain,
count(),
any(Title) AS title -- для каждого домена достаём первый попавшийся заголовок страницы
FROM hits
GROUP BY domain
GROUP BY вычисляет для каждого встретившегося различного значения ключей, набор значений агрегатных функций.
Не поддерживается GROUP BY по столбцам-массивам.
Не поддерживается указание констант в качестве аргументов агрегатных функций. Пример: sum(1). Вместо этого, вы можете избавиться от констант. Пример: ``count()``.
Модификатор WITH TOTALS
^^^^^^^^^^^^^^^^^^^^^^^
Если указан модификатор WITH TOTALS, то будет посчитана ещё одна строчка, в которой в столбцах-ключах будут содержаться значения по умолчанию (нули, пустые строки), а в столбцах агрегатных функций - значения, посчитанные по всем строкам ("тотальные" значения).
Эта дополнительная строчка выводится в форматах JSON*, TabSeparated*, Pretty* отдельно от остальных строчек. В остальных форматах эта строчка не выводится.
В форматах JSON* строчка выводится отдельным полем totals. В форматах TabSeparated* строчка выводится после основного результата, и перед ней (после остальных данных) вставляется пустая строка. В форматах Pretty* строчка выводится отдельной табличкой после основного результата.
``WITH TOTALS`` может выполняться по-разному при наличии HAVING. Поведение зависит от настройки totals_mode.
По умолчанию ``totals_mode = 'before_having'``. В этом случае totals считается по всем строчкам, включая непрошедших через HAVING и max_rows_to_group_by.
Остальные варианты учитывают в totals только строчки, прошедшие через HAVING, и имеют разное поведение при наличии настройки ``max_rows_to_group_by`` и ``group_by_overflow_mode = 'any'``.
``after_having_exclusive`` - не учитывать строчки, не прошедшие ``max_rows_to_group_by``. То есть в totals попадёт меньше или столько же строчек, чем если бы ``max_rows_to_group_by`` не было.
``after_having_inclusive`` - учитывать в totals все строчки, не прошедшие max_rows_to_group_by. То есть в totals попадёт больше или столько же строчек, чем если бы ``max_rows_to_group_by`` не было.
``after_having_auto`` - считать долю строчек, прошедших через HAVING. Если она больше некоторого значения (по умолчанию - 50%), то включить все строчки, не прошедшие max_rows_to_group_by в totals, иначе - не включить.
``totals_auto_threshold`` - по умолчанию 0.5. Коэффициент для работы ``after_having_auto``.
Если ``max_rows_to_group_by`` и ``group_by_overflow_mode = 'any'`` не используются, то все варианты вида ``after_having`` не отличаются, и вы можете использовать любой из них, например, ``after_having_auto``.
Вы можете использовать WITH TOTALS в подзапросах, включая подзапросы в секции JOIN (в этом случае соответствующие тотальные значения будут соединены).
GROUP BY во внешней памяти
^^^^^^^^^^^^^^^^^^^^^^^^^^
Существует возможность включить сброс временных данных на диск для ограничения потребления оперативной памяти при GROUP BY.
Настройка ``max_bytes_before_external_group_by`` - потребление оперативки, при котором временные данные GROUP BY сбрасываются в файловую систему. Если равно 0 (по умолчанию) - значит выключено.
При использовании ``max_bytes_before_external_group_by`` рекомендуется выставить max_memory_usage примерно в два раза больше. Это следует сделать, потому что агрегация выполняется в две стадии: чтение и формирование промежуточных данных (1) и слияние промежуточных данных (2). Сброс данных на файловую систему может производиться только на стадии 1. Если сброса временных данных не было, то на стадии 2 может потребляться до такого же объёма памяти, как на стадии 1.
Например, если у вас ``max_memory_usage`` было выставлено в 10000000000, и вы хотите использовать внешнюю агрегацию, то имеет смысл выставить ``max_bytes_before_external_group_by`` в 10000000000, а max_memory_usage в 20000000000. При срабатывании внешней агрегации (если был хотя бы один сброс временных данных в файловую систему) максимальное потребление оперативки будет лишь чуть-чуть больше ``max_bytes_before_external_group_by``.
При распределённой обработке запроса внешняя агрегация производится на удалённых серверах. Для того чтобы на сервере-инициаторе запроса использовалось немного оперативки, нужно выставить настройку ``distributed_aggregation_memory_efficient`` в 1.
При слиянии данных, сброшенных на диск, а также при слиянии результатов с удалённых серверов, при включенной настройке ``distributed_aggregation_memory_efficient``, потребляется до 1/256 * количество потоков от общего объёма оперативки.
При включенной внешней агрегации, если данных было меньше ``max_bytes_before_external_group_by`` (то есть сброса данных не было), то запрос работает так же быстро, как без внешней агрегации. Если же какие-то временные данные были сброшены, то время выполнения будет в несколько раз больше (примерно в три раза).
Если после GROUP BY у вас есть ORDER BY с небольшим LIMIT, то на ORDER BY не будет тратиться существенного количества оперативки.
Но если есть ORDER BY без LIMIT, то не забудьте включить внешнюю сортировку (``max_bytes_before_external_sort``).
Модификатор LIMIT N BY
^^^^^^^^^^^^^^^^^^^^^^
LIMIT N BY COLUMNS позволяет выбрать топ N строк для каждой группы COLUMNS. LIMIT N BY не связан с LIMIT и они могут использоваться в одном запросе. Ключ для LIMIT N BY может содержать произвольное число колонок или выражений.
Пример:
.. code-block:: sql
SELECT
domainWithoutWWW(URL) AS domain,
domainWithoutWWW(REFERRER_URL) AS referrer,
device_type,
count() cnt
FROM hits
GROUP BY domain, referrer, device_type
ORDER BY cnt DESC
LIMIT 5 BY domain, device_type
LIMIT 100
выберет топ 5 рефереров для каждой пары domain - device type. Ограничить общее число строк результата 100.
Секция HAVING
"""""""""""""
Позволяет отфильтровать результат, полученный после GROUP BY, аналогично секции WHERE.
WHERE и HAVING отличаются тем, что WHERE выполняется до агрегации (GROUP BY), а HAVING - после.
Если агрегации не производится, то HAVING использовать нельзя.
Секция ORDER BY
"""""""""""""""
Секция ORDER BY содержит список выражений, к каждому из которых также может быть приписано DESC или ASC (направление сортировки). Если ничего не приписано - это аналогично приписыванию ASC. ASC - сортировка по возрастанию, DESC - сортировка по убыванию. Обозначение направления сортировки действует на одно выражение, а не на весь список. Пример: ``ORDER BY Visits DESC, SearchPhrase``
Для сортировки по значениям типа String есть возможность указать collation (сравнение). Пример: ``ORDER BY SearchPhrase COLLATE 'tr'`` - для сортировки по поисковой фразе, по возрастанию, с учётом турецкого алфавита, регистронезависимо, при допущении, что строки в кодировке UTF-8. COLLATE может быть указан или не указан для каждого выражения в ORDER BY независимо. Если есть ASC или DESC, то COLLATE указывается после них. При использовании COLLATE сортировка всегда регистронезависима.
Рекомендуется использовать COLLATE только для окончательной сортировки небольшого количества строк, так как производительность сортировки с указанием COLLATE меньше, чем обычной сортировки по байтам.
Строки, для которых список выражений, по которым производится сортировка, принимает одинаковые значения, выводятся в произвольном порядке, который может быть также недетерминированным (каждый раз разным).
Если секция ORDER BY отсутствует, то, аналогично, порядок, в котором идут строки, не определён, и может быть недетерминированным.
При сортировке чисел с плавающей запятой, NaN-ы идут отдельно от остальных значений. Вне зависимости от порядка сортировки, NaN-ы помещаются в конец. То есть, при сортировке по возрастанию, они как будто больше всех чисел, а при сортировке по убыванию - как будто меньше всех.
Если кроме ORDER BY указан также не слишком большой LIMIT, то расходуется меньше оперативки. Иначе расходуется количество памяти, пропорциональное количеству данных для сортировки. При распределённой обработке запроса, если отсутствует GROUP BY, сортировка частично делается на удалённых серверах, а на сервере-инициаторе запроса производится слияние результатов. Таким образом, при распределённой сортировке, может сортироваться объём данных, превышающий размер памяти на одном сервере.
Существует возможность выполнять сортировку во внешней памяти (с созданием временных файлов на диске), если оперативной памяти не хватает. Для этого предназначена настройка ``max_bytes_before_external_sort``. Если она выставлена в 0 (по умолчанию), то внешняя сортировка выключена. Если она включена, то при достижении объёмом данных для сортировки указанного количества байт, накопленные данные будут отсортированы и сброшены во временный файл. После того, как все данные будут прочитаны, будет произведено слияние всех сортированных файлов и выдача результата. Файлы записываются в директорию /var/lib/clickhouse/tmp/ (по умолчанию, может быть изменено с помощью параметра tmp_path) в конфиге.
На выполнение запроса может расходоваться больше памяти, чем max_bytes_before_external_sort. Поэтому, значение этой настройки должно быть существенно меньше, чем max_memory_usage. Для примера, если на вашем сервере 128 GB оперативки, и вам нужно выполнить один запрос, то выставите max_memory_usage в 100 GB, а max_bytes_before_external_sort в 80 GB.
Внешняя сортировка работает существенно менее эффективно, чем сортировка в оперативке.
Секция SELECT
"""""""""""""
После вычислений, соответствующих всем перечисленным выше секциям, производится вычисление выражений, указанных в секции SELECT.
Вернее, вычисляются выражения, стоящие над агрегатными функциями, если есть агрегатные функции.
Сами агрегатные функции и то, что под ними, вычисляются при агрегации (GROUP BY).
Эти выражения работают так, как будто применяются к отдельным строкам результата.
Секция DISTINCT
"""""""""""""""
Если указано DISTINCT, то из всех множеств полностью совпадающих строк результата, будет оставляться только одна строка.
Результат выполнения будет таким же, как если указано GROUP BY по всем указанным полям в SELECT-е и не указаны агрегатные функции. Но имеется несколько отличий от GROUP BY:
- DISTINCT может применяться совместно с GROUP BY;
- при отсутствии ORDER BY и наличии LIMIT, запрос прекратит выполнение сразу после того, как будет прочитано необходимое количество различных строк - в этом случае использование DISTINCT существенно более оптимально;
- блоки данных будут выдаваться по мере их обработки, не дожидаясь выполнения всего запроса.
DISTINCT не поддерживается, если в SELECT-е присутствует хотя бы один столбец типа массив.
Секция LIMIT
""""""""""""
LIMIT m позволяет выбрать из результата первые m строк.
LIMIT n, m позволяет выбрать из результата первые m строк после пропуска первых n строк.
n и m должны быть неотрицательными целыми числами.
При отсутствии секции ORDER BY, однозначно сортирующей результат, результат может быть произвольным и может являться недетерминированным.
Секция UNION ALL
""""""""""""""""
Произвольное количество запросов может быть объединено с помощью UNION ALL. Пример:
.. code-block:: sql
SELECT CounterID, 1 AS table, toInt64(count()) AS c
FROM test.hits
GROUP BY CounterID
UNION ALL
SELECT CounterID, 2 AS table, sum(Sign) AS c
FROM test.visits
GROUP BY CounterID
HAVING c > 0
Поддерживается только UNION ALL. Обычный UNION (UNION DISTINCT) не поддерживается. Если вам нужен UNION DISTINCT, то вы можете написать SELECT DISTINCT из подзапроса, содержащего UNION ALL.
Запросы - части UNION ALL могут выполняться параллельно, и их результаты могут возвращаться вперемешку.
Структура результатов (количество и типы столбцов) у запросов должна совпадать. Но имена столбцов могут отличаться. В этом случае, имена столбцов для общего результата будут взяты из первого запроса.
Запросы - части UNION ALL нельзя заключить в скобки. ORDER BY и LIMIT применяются к отдельным запросам, а не к общему результату. Если вам нужно применить какое-либо преобразование к общему результату, то вы можете разместить все запросы с UNION ALL в подзапросе в секции FROM.
Секция INTO OUTFILE
"""""""""""""""""""
При указании ``INTO OUTFILE filename`` (где filename - строковый литерал), результат запроса будет сохранён в файл filename.
В отличие от MySQL, файл создаётся на стороне клиента. Если файл с таким именем уже существует, это приведёт к ошибке.
Функциональность доступна в клиенте командной строки и clickhouse-local (попытка выполнить запрос с INTO OUTFILE через HTTP интерфейс приведёт к ошибке).
Формат вывода по умолчанию - TabSeparated, как и в неинтерактивном режиме клиента командной строки.
Секция FORMAT
"""""""""""""
При указании FORMAT format вы можете получить данные в любом указанном формате.
Это может использоваться для удобства или для создания дампов.
Подробнее смотрите раздел "Форматы".
Если секция FORMAT отсутствует, то используется формат по умолчанию, который зависит от используемого интерфейса для доступа к БД и от настроек. Для HTTP интерфейса, а также для клиента командной строки, используемого в batch-режиме, по умолчанию используется формат TabSeparated. Для клиента командной строки, используемого в интерактивном режиме, по умолчанию используется формат PrettyCompact (прикольные таблички, компактные).
При использовании клиента командной строки данные на клиент передаются во внутреннем эффективном формате. При этом клиент самостоятельно интерпретирует секцию FORMAT запроса и форматирует данные на своей стороне (снимая нагрузку на сеть и сервер).
Операторы IN
""""""""""""
Операторы ``IN``, ``NOT IN``, ``GLOBAL IN``, ``GLOBAL NOT IN`` рассматриваются отдельно, так как их функциональность достаточно богатая.
В качестве левой части оператора, может присутствовать как один столбец, так и кортеж.
Примеры:
.. code-block:: sql
SELECT UserID IN (123, 456) FROM ...
SELECT (CounterID, UserID) IN ((34, 123), (101500, 456)) FROM ...
Если слева стоит один столбец, входящий в индекс, а справа - множество констант, то при выполнении запроса, система воспользуется индексом.
Не перечисляйте слишком большое количество значений (миллионы) явно. Если множество большое - лучше загрузить его во временную таблицу (например, смотрите раздел "Внешние данные для обработки запроса"), и затем воспользоваться подзапросом.
В качестве правой части оператора может быть множество константных выражений, множество кортежей с константными выражениями (показано в примерах выше), а также имя таблицы или подзапрос SELECT в скобках.
Если в качестве правой части оператора указано имя таблицы (например, ``UserID IN users``), то это эквивалентно подзапросу ``UserID IN (SELECT * FROM users)``. Это используется при работе с внешними данными, отправляемым вместе с запросом. Например, вместе с запросом может быть отправлено множество идентификаторов посетителей, загруженное во временную таблицу users, по которому следует выполнить фильтрацию.
Если качестве правой части оператора, указано имя таблицы, имеющий движок Set (подготовленное множество, постоянно находящееся в оперативке), то множество не будет создаваться заново при каждом запросе.
В подзапросе может быть указано более одного столбца для фильтрации кортежей.
Пример:
.. code-block:: sql
SELECT (CounterID, UserID) IN (SELECT CounterID, UserID FROM ...) FROM ...
Типы столбцов слева и справа оператора IN, должны совпадать.
Оператор IN и подзапрос могут встречаться в любой части запроса, в том числе в агрегатных и лямбда функциях.
Пример:
.. code-block:: sql
SELECT
EventDate,
avg(UserID IN
(
SELECT UserID
FROM test.hits
WHERE EventDate = toDate('2014-03-17')
)) AS ratio
FROM test.hits
GROUP BY EventDate
ORDER BY EventDate ASC
EventDateratio
2014-03-17 1
2014-03-18 0.807696
2014-03-19 0.755406
2014-03-20 0.723218
2014-03-21 0.697021
2014-03-22 0.647851
2014-03-23 0.648416
- за каждый день после 17 марта считаем долю хитов, сделанных посетителями, которые заходили на сайт 17 марта.
Подзапрос в секции IN на одном сервере всегда выполняется только один раз. Зависимых подзапросов не существует.
Распределённые подзапросы
"""""""""""""""""""""""""
Существует два варианта IN-ов с подзапросами (аналогично для JOIN-ов): обычный ``IN`` / ``JOIN`` и ``GLOBAL IN`` / ``GLOBAL JOIN``. Они отличаются способом выполнения при распределённой обработке запроса.
При использовании обычного IN-а, запрос отправляется на удалённые серверы, и на каждом из них выполняются подзапросы в секциях ``IN`` / ``JOIN``.
При использовании ``GLOBAL IN`` / ``GLOBAL JOIN-а``, сначала выполняются все подзапросы для ``GLOBAL IN`` / ``GLOBAL JOIN-ов``, и результаты складываются во временные таблицы. Затем эти временные таблицы передаются на каждый удалённый сервер, и на них выполняются запросы, с использованием этих переданных временных данных.
Если запрос не распределённый, используйте обычный ``IN`` / ``JOIN``.
Следует быть внимательным при использовании подзапросов в секции ``IN`` / ``JOIN`` в случае распределённой обработки запроса.
Рассмотрим это на примерах. Пусть на каждом сервере кластера есть обычная таблица **local_table**. Пусть также есть таблица **distributed_table** типа **Distributed**, которая смотрит на все серверы кластера.
При запросе к распределённой таблице **distributed_table**, запрос будет отправлен на все удалённые серверы, и на них будет выполнен с использованием таблицы **local_table**.
Например, запрос
``SELECT uniq(UserID) FROM distributed_table``
будет отправлен на все удалённые серверы в виде
``SELECT uniq(UserID) FROM local_table``
, выполнен параллельно на каждом из них до стадии, позволяющей объединить промежуточные результаты; затем промежуточные результаты вернутся на сервер-инициатор запроса, будут на нём объединены, и финальный результат будет отправлен клиенту.
Теперь рассмотрим запрос с IN-ом:
.. code-block:: sql
SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM local_table WHERE CounterID = 34)
- расчёт пересечения аудиторий двух сайтов.
Этот запрос будет отправлен на все удалённые серверы в виде
.. code-block:: sql
SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM local_table WHERE CounterID = 34)
То есть, множество в секции IN будет собрано на каждом сервере независимо, только по тем данным, которые есть локально на каждом из серверов.
Это будет работать правильно и оптимально, если вы предусмотрели такой случай, и раскладываете данные по серверам кластера таким образом, чтобы данные одного UserID-а лежали только на одном сервере. В таком случае все необходимые данные будут присутствовать на каждом сервере локально. В противном случае результат будет посчитан неточно. Назовём этот вариант запроса "локальный IN".
Чтобы исправить работу запроса, когда данные размазаны по серверам кластера произвольным образом, можно было бы указать **distributed_table** внутри подзапроса. Запрос будет выглядеть так:
.. code-block:: sql
SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)
Этот запрос будет отправлен на все удалённые серверы в виде
.. code-block:: sql
SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)
На каждом удалённом сервере начнёт выполняться подзапрос. Так как в подзапросе используется распределённая таблица, то подзапрос будет, на каждом удалённом сервере, снова отправлен на каждый удалённый сервер, в виде
.. code-block:: sql
SELECT UserID FROM local_table WHERE CounterID = 34
Например, если у вас кластер из 100 серверов, то выполнение всего запроса потребует 10 000 элементарных запросов, что, как правило, является неприемлемым.
В таких случаях всегда следует использовать GLOBAL IN вместо IN. Рассмотрим его работу для запроса
.. code-block:: sql
SELECT uniq(UserID) FROM distributed_table WHERE CounterID = 101500 AND UserID GLOBAL IN (SELECT UserID FROM distributed_table WHERE CounterID = 34)
На сервере-инициаторе запроса будет выполнен подзапрос
.. code-block:: sql
SELECT UserID FROM distributed_table WHERE CounterID = 34
, и результат будет сложен во временную таблицу в оперативке. Затем запрос будет отправлен на каждый удалённый сервер в виде
.. code-block:: sql
SELECT uniq(UserID) FROM local_table WHERE CounterID = 101500 AND UserID GLOBAL IN _data1
, и вместе с запросом, на каждый удалённый сервер будет отправлена временная таблица _data1 (имя временной таблицы - implementation defined).
Это гораздо более оптимально, чем при использовании обычного IN. Но при этом, следует помнить о нескольких вещах:
#. При создании временной таблицы данные не уникализируются. Чтобы уменьшить объём передаваемых по сети данных, укажите в подзапросе DISTINCT (для обычного IN-а этого делать не нужно).
#. Временная таблица будет передана на все удалённые серверы. Передача не учитывает топологию сети. Например, если 10 удалённых серверов расположены в удалённом относительно сервера-инициатора запроса датацентре, то по каналу в удалённый датацентр данные будет переданы 10 раз. Старайтесь не использовать большие множества при использовании GLOBAL IN.
#. При передаче данных на удалённые серверы не настраивается ограничение использования сетевой полосы. Вы можете перегрузить сеть.
#. Старайтесь распределять данные по серверам так, чтобы в GLOBAL IN-ах не было частой необходимости.
#. Если в GLOBAL IN есть частая необходимость, то спланируйте размещение кластера ClickHouse таким образом, чтобы в каждом датацентре была хотя бы одна реплика каждого шарда, и среди них была быстрая сеть - чтобы запрос целиком можно было бы выполнить, передавая данные в пределах одного датацентра.
В секции ``GLOBAL IN`` также имеет смысл указывать локальную таблицу - в случае, если эта локальная таблица есть только на сервере-инициаторе запроса, и вы хотите воспользоваться данными из неё на удалённых серверах.
Экстремальные значения
""""""""""""""""""""""
Вы можете получить в дополнение к результату также минимальные и максимальные значения по столбцам результата. Для этого выставите настройку **extremes** в 1. Минимумы и максимумы считаются для числовых типов, дат, дат-с-временем. Для остальных столбцов будут выведены значения по умолчанию.
Вычисляются дополнительные две строчки - минимумы и максимумы, соответственно. Эти дополнительные две строчки выводятся в форматах JSON*, TabSeparated*, Pretty* отдельно от остальных строчек. В остальных форматах они не выводится.
В форматах JSON* экстремальные значения выводятся отдельным полем extremes. В форматах TabSeparated* строчка выводится после основного результата и после totals, если есть. Перед ней (после остальных данных) вставляется пустая строка. В форматах Pretty* строчка выводится отдельной табличкой после основного результата и после totals, если есть.
Экстремальные значения считаются по строчкам, прошедшим через LIMIT. Но при этом, при использовании LIMIT offset, size, строчки до offset учитываются в extremes. В потоковых запросах, в результате может учитываться также небольшое количество строчек, прошедших LIMIT.
Замечания
"""""""""
В секциях ``GROUP BY``, ``ORDER BY``, в отличие от диалекта MySQL, и в соответствии со стандартным SQL, не поддерживаются позиционные аргументы.
Например, если вы напишите ``GROUP BY 1, 2`` - то это будет воспринято, как группировка по константам (то есть, агрегация всех строк в одну).
Вы можете использовать синонимы (алиасы ``AS``) в любом месте запроса.
В любом месте запроса, вместо выражения, может стоять звёздочка. При анализе запроса звёздочка раскрывается в список всех столбцов таблицы (за исключением ``MATERIALIZED`` и ``ALIAS`` столбцов). Есть лишь немного случаев, когда оправдано использовать звёздочку:
* при создании дампа таблицы;
* для таблиц, содержащих всего несколько столбцов - например, системных таблиц;
* для получения информации о том, какие столбцы есть в таблице; в этом случае, укажите ``LIMIT 1``. Но лучше используйте запрос ``DESC TABLE``;
* при наличии сильной фильтрации по небольшому количеству столбцов с помощью ``PREWHERE``;
* в подзапросах (так как из подзапросов выкидываются столбцы, не нужные для внешнего запроса).
В других случаях использование звёздочки является издевательством над системой, так как вместо преимуществ столбцовой СУБД вы получаете недостатки. То есть использовать звёздочку не рекомендуется.
KILL QUERY
~~~~~~~~~~
.. code-block:: sql
KILL QUERY WHERE <where expression to SELECT FROM system.processes query> [SYNC|ASYNC|TEST] [FORMAT format]
Пытается завершить исполняющиеся в данный момент запросы.
Запросы для завершения выбираются из таблицы system.processes для которых выражение expression_for_system.processes истинно.
Примеры:
.. code-block:: sql
KILL QUERY WHERE query_id='2-857d-4a57-9ee0-327da5d60a90'
Завершает все запросы с указанным query_id.
.. code-block:: sql
KILL QUERY WHERE user='username' SYNC
Синхронно завершает все запросы пользователя ``username``.
Readonly-пользователи могут совершать только свои запросы.
По-умолчанию используется асинхронный вариант запроса (``ASYNC``), который завершается не ожидая завершения запросов.
Синхронный вариант (``SYNC``) ожидает завершения всех запросов и построчно выводит информацию о процессах по ходу их завершения.
Ответ содержит колонку ``kill_status``, которая может принимать следующие значения:
#. 'finished' - запрос успешно завершился;
#. 'waiting' - запросу отправлен сигнал завершения, ожидается его завершение;
#. остальные значения описывают причину невозможности завершения запроса.
Тестовый вариант запроса (``TEST``) только проверяет права пользователя и выводит список запросов для завершения.