13.1.20.5 FOREIGN KEY Constraints Show MySQL supports foreign keys, which permit cross-referencing related data across tables, and foreign key constraints, which help keep the related data consistent. A foreign key relationship involves a parent table that holds the initial column values, and a child table with column values that reference the parent column values. A foreign key constraint is defined on the child table. The essential syntax for a defining a foreign key constraint in a CREATE TABLE or ALTER TABLE statement includes the following: [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (col_name, ...) REFERENCES tbl_name (col_name,...) [ON DELETE reference_option] [ON UPDATE reference_option] reference_option: RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULTForeign key constraint usage is described under the following topics in this section:
Foreign key constraint naming is governed by the following rules:
Table and column identifiers in a FOREIGN KEY ... REFERENCES clause can be quoted within backticks (`). Alternatively, double quotation marks (") can be used if the ANSI_QUOTES SQL mode is enabled. The lower_case_table_names system variable setting is also taken into account.
Conditions and RestrictionsForeign key constraints are subject to the following conditions and restrictions:
For information about how the MySQL implementation of foreign key constraints differs from the SQL standard, see Section 1.6.2.3, “FOREIGN KEY Constraint Differences”.
When an UPDATE or DELETE operation affects a key value in the parent table that has matching rows in the child table, the result depends on the referential action specified by ON UPDATE and ON DELETE subclauses of the FOREIGN KEY clause. Referential actions include:
For storage engines that support foreign keys, MySQL rejects any INSERT or UPDATE operation that attempts to create a foreign key value in a child table if there is no matching candidate key value in the parent table. For an ON DELETE or ON UPDATE that is not specified, the default action is always NO ACTION. As the default, an ON DELETE NO ACTION or ON UPDATE NO ACTION clause that is specified explicitly does not appear in SHOW CREATE TABLE output or in tables dumped with mysqldump. RESTRICT, which is an equivalent non-default keyword, appears in SHOW CREATE TABLE output and in tables dumped with mysqldump. For NDB tables, ON UPDATE CASCADE is not supported where the reference is to the parent table's primary key. As of NDB 8.0.16: For NDB tables, ON DELETE CASCADE is not supported where the child table contains one or more columns of any of the TEXT or BLOB types. (Bug #89511, Bug #27484882) InnoDB performs cascading operations using a depth-first search algorithm on the records of the index that corresponds to the foreign key constraint. A foreign key constraint on a stored generated column cannot use CASCADE, SET NULL, or SET DEFAULT as ON UPDATE referential actions, nor can it use SET NULL or SET DEFAULT as ON DELETE referential actions. A foreign key constraint on the base column of a stored generated column cannot use CASCADE, SET NULL, or SET DEFAULT as ON UPDATE or ON DELETE referential actions.
Foreign Key Constraint ExamplesThis simple example relates parent and child tables through a single-column foreign key: CREATE TABLE parent ( id INT NOT NULL, PRIMARY KEY (id) ) ENGINE=INNODB; CREATE TABLE child ( id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ) ENGINE=INNODB;This is a more complex example in which a product_order table has foreign keys for two other tables. One foreign key references a two-column index in the product table. The other references a single-column index in the customer table: CREATE TABLE product ( category INT NOT NULL, id INT NOT NULL, price DECIMAL, PRIMARY KEY(category, id) ) ENGINE=INNODB; CREATE TABLE customer ( id INT NOT NULL, PRIMARY KEY (id) ) ENGINE=INNODB; CREATE TABLE product_order ( no INT NOT NULL AUTO_INCREMENT, product_category INT NOT NULL, product_id INT NOT NULL, customer_id INT NOT NULL, PRIMARY KEY(no), INDEX (product_category, product_id), INDEX (customer_id), FOREIGN KEY (product_category, product_id) REFERENCES product(category, id) ON UPDATE CASCADE ON DELETE RESTRICT, FOREIGN KEY (customer_id) REFERENCES customer(id) ) ENGINE=INNODB;
Adding Foreign Key ConstraintsYou can add a foreign key constraint to an existing table using the following ALTER TABLE syntax: ALTER TABLE tbl_name ADD [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (col_name, ...) REFERENCES tbl_name (col_name,...) [ON DELETE reference_option] [ON UPDATE reference_option]The foreign key can be self referential (referring to the same table). When you add a foreign key constraint to a table using ALTER TABLE, remember to first create an index on the column(s) referenced by the foreign key.
Dropping Foreign Key ConstraintsYou can drop a foreign key constraint using the following ALTER TABLE syntax: ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;If the FOREIGN KEY clause defined a CONSTRAINT name when you created the constraint, you can refer to that name to drop the foreign key constraint. Otherwise, a constraint name was generated internally, and you must use that value. To determine the foreign key constraint name, use SHOW CREATE TABLE: mysql> SHOW CREATE TABLE child\G *************************** 1. row *************************** Table: child Create Table: CREATE TABLE `child` ( `id` int DEFAULT NULL, `parent_id` int DEFAULT NULL, KEY `par_ind` (`parent_id`), CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci mysql> ALTER TABLE child DROP FOREIGN KEY `child_ibfk_1`;Adding and dropping a foreign key in the same ALTER TABLE statement is supported for ALTER TABLE ... ALGORITHM=INPLACE. It is not supported for ALTER TABLE ... ALGORITHM=COPY.
In MySQL, InnoDB and NDB tables support checking of foreign key constraints. Foreign key checking is controlled by the foreign_key_checks variable, which is enabled by default. Typically, you leave this variable enabled during normal operation to enforce referential integrity. The foreign_key_checks variable has the same effect on NDB tables as it does for InnoDB tables. The foreign_key_checks variable is dynamic and supports both global and session scopes. For information about using system variables, see Section 5.1.9, “Using System Variables”. Disabling foreign key checking is useful when:
When foreign_key_checks is disabled, foreign key constraints are ignored, with the following exceptions:
Disabling foreign_key_checks has these additional implications:
MySQL extends metadata locks, as necessary, to tables that are related by a foreign key constraint. Extending metadata locks prevents conflicting DML and DDL operations from executing concurrently on related tables. This feature also enables updates to foreign key metadata when a parent table is modified. In earlier MySQL releases, foreign key metadata, which is owned by the child table, could not be updated safely. If a table is locked explicitly with LOCK TABLES, any tables related by a foreign key constraint are opened and locked implicitly. For foreign key checks, a shared read-only lock (LOCK TABLES READ) is taken on related tables. For cascading updates, a shared-nothing write lock (LOCK TABLES WRITE) is taken on related tables that are involved in the operation.
Foreign Key Definitions and MetadataTo view a foreign key definition, use SHOW CREATE TABLE: mysql> SHOW CREATE TABLE child\G *************************** 1. row *************************** Table: child Create Table: CREATE TABLE `child` ( `id` int DEFAULT NULL, `parent_id` int DEFAULT NULL, KEY `par_ind` (`parent_id`), CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ciYou can obtain information about foreign keys from the INFORMATION_SCHEMA.KEY_COLUMN_USAGE table. An example of a query against this table is shown here: mysql> SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA IS NOT NULL; +--------------+------------+-------------+-----------------+ | TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | CONSTRAINT_NAME | +--------------+------------+-------------+-----------------+ | test | child | parent_id | child_ibfk_1 | +--------------+------------+-------------+-----------------+You can obtain information specific to InnoDB foreign keys from the INNODB_FOREIGN and INNODB_FOREIGN_COLS tables. Example queries are show here: mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_FOREIGN \G *************************** 1. row *************************** ID: test/child_ibfk_1 FOR_NAME: test/child REF_NAME: test/parent N_COLS: 1 TYPE: 1 mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_FOREIGN_COLS \G *************************** 1. row *************************** ID: test/child_ibfk_1 FOR_COL_NAME: parent_id REF_COL_NAME: id POS: 0
In the event of a foreign key error involving InnoDB tables (usually Error 150 in the MySQL Server), information about the latest foreign key error can be obtained by checking SHOW ENGINE INNODB STATUS output. mysql> SHOW ENGINE INNODB STATUS\G ... ------------------------ LATEST FOREIGN KEY ERROR ------------------------ 2018-04-12 14:57:24 0x7f97a9c91700 Transaction: TRANSACTION 7717, ACTIVE 0 sec inserting mysql tables in use 1, locked 1 4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 3 MySQL thread id 8, OS thread handle 140289365317376, query id 14 localhost root update INSERT INTO child VALUES (NULL, 1), (NULL, 2), (NULL, 3), (NULL, 4), (NULL, 5), (NULL, 6) Foreign key constraint fails for table `test`.`child`: , CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE ON UPDATE CASCADE Trying to add in child table, in index par_ind tuple: DATA TUPLE: 2 fields; 0: len 4; hex 80000003; asc ;; 1: len 4; hex 80000003; asc ;; But in parent table `test`.`parent`, in index PRIMARY, the closest match we can find is record: PHYSICAL RECORD: n_fields 3; compact format; info bits 0 0: len 4; hex 80000004; asc ;; 1: len 6; hex 000000001e19; asc ;; 2: len 7; hex 81000001110137; asc 7;; ...
Warning If a user has table-level privileges for all parent tables, ER_NO_REFERENCED_ROW_2 and ER_ROW_IS_REFERENCED_2 error messages for foreign key operations expose information about parent tables. If a user does not have table-level privileges for all parent tables, more generic error messages are displayed instead (ER_NO_REFERENCED_ROW and ER_ROW_IS_REFERENCED). An exception is that, for stored programs defined to execute with DEFINER privileges, the user against which privileges are assessed is the user in the program DEFINER clause, not the invoking user. If that user has table-level parent table privileges, parent table information is still displayed. In this case, it is the responsibility of the stored program creator to hide the information by including appropriate condition handlers. Page 2
13.1.20.6 CHECK ConstraintsPrior to MySQL 8.0.16, CREATE TABLE permits only the following limited version of table CHECK constraint syntax, which is parsed and ignored: CHECK (expr)As of MySQL 8.0.16, CREATE TABLE permits the core features of table and column CHECK constraints, for all storage engines. CREATE TABLE permits the following CHECK constraint syntax, for both table constraints and column constraints: [CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED]The optional symbol specifies a name for the constraint. If omitted, MySQL generates a name from the table name, a literal _chk_, and an ordinal number (1, 2, 3, ...). Constraint names have a maximum length of 64 characters. They are case-sensitive, but not accent-sensitive. expr specifies the constraint condition as a boolean expression that must evaluate to TRUE or UNKNOWN (for NULL values) for each row of the table. If the condition evaluates to FALSE, it fails and a constraint violation occurs. The effect of a violation depends on the statement being executed, as described later in this section. The optional enforcement clause indicates whether the constraint is enforced:
A CHECK constraint is specified as either a table constraint or column constraint:
Consider this table definition: CREATE TABLE t1 ( CHECK (c1 <> c2), c1 INT CHECK (c1 > 10), c2 INT CONSTRAINT c2_positive CHECK (c2 > 0), c3 INT CHECK (c3 < 100), CONSTRAINT c1_nonzero CHECK (c1 <> 0), CHECK (c1 > c3) );The definition includes table constraints and column constraints, in named and unnamed formats:
As mentioned, MySQL generates a name for any CHECK constraint specified without one. To see the names generated for the preceding table definition, use SHOW CREATE TABLE: mysql> SHOW CREATE TABLE t1\G *************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL, `c3` int(11) DEFAULT NULL, CONSTRAINT `c1_nonzero` CHECK ((`c1` <> 0)), CONSTRAINT `c2_positive` CHECK ((`c2` > 0)), CONSTRAINT `t1_chk_1` CHECK ((`c1` <> `c2`)), CONSTRAINT `t1_chk_2` CHECK ((`c1` > 10)), CONSTRAINT `t1_chk_3` CHECK ((`c3` < 100)), CONSTRAINT `t1_chk_4` CHECK ((`c1` > `c3`)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ciThe SQL standard specifies that all types of constraints (primary key, unique index, foreign key, check) belong to the same namespace. In MySQL, each constraint type has its own namespace per schema (database). Consequently, CHECK constraint names must be unique per schema; no two tables in the same schema can share a CHECK constraint name. (Exception: A TEMPORARY table hides a non-TEMPORARY table of the same name, so it can have the same CHECK constraint names as well.) Beginning generated constraint names with the table name helps ensure schema uniqueness because table names also must be unique within the schema. CHECK condition expressions must adhere to the following rules. An error occurs if an expression contains disallowed constructs.
Foreign key referential actions (ON UPDATE, ON DELETE) are prohibited on columns used in CHECK constraints. Likewise, CHECK constraints are prohibited on columns used in foreign key referential actions. CHECK constraints are evaluated for INSERT, UPDATE, REPLACE, LOAD DATA, and LOAD XML statements and an error occurs if a constraint evaluates to FALSE. If an error occurs, handling of changes already applied differs for transactional and nontransactional storage engines, and also depends on whether strict SQL mode is in effect, as described in Strict SQL Mode. CHECK constraints are evaluated for INSERT IGNORE, UPDATE IGNORE, LOAD DATA ... IGNORE, and LOAD XML ... IGNORE statements and a warning occurs if a constraint evaluates to FALSE. The insert or update for any offending row is skipped. If the constraint expression evaluates to a data type that differs from the declared column type, implicit coercion to the declared type occurs according to the usual MySQL type-conversion rules. See Section 12.3, “Type Conversion in Expression Evaluation”. If type conversion fails or results in a loss of precision, an error occurs.
Note Constraint expression evaluation uses the SQL mode in effect at evaluation time. If any component of the expression depends on the SQL mode, different results may occur for different uses of the table unless the SQL mode is the same during all uses. The INFORMATION_SCHEMA.CHECK_CONSTRAINTS table provides information about CHECK constraints defined on tables. See Section 26.3.5, “The INFORMATION_SCHEMA CHECK_CONSTRAINTS Table”. Page 3
13.1.20.7 Silent Column Specification ChangesIn some cases, MySQL silently changes column specifications from those given in a CREATE TABLE or ALTER TABLE statement. These might be changes to a data type, to attributes associated with a data type, or to an index specification. All changes are subject to the internal row-size limit of 65,535 bytes, which may cause some attempts at data type changes to fail. See Section 8.4.7, “Limits on Table Column Count and Row Size”.
To see whether MySQL used a data type other than the one you specified, issue a DESCRIBE or SHOW CREATE TABLE statement after creating or altering the table. Certain other data type changes can occur if you compress a table using myisampack. See Section 16.2.3.3, “Compressed Table Characteristics”. Page 4
13.1.20.8 CREATE TABLE and Generated ColumnsCREATE TABLE supports the specification of generated columns. Values of a generated column are computed from an expression included in the column definition. Generated columns are also supported by the NDB storage engine. The following simple example shows a table that stores the lengths of the sides of right triangles in the sidea and sideb columns, and computes the length of the hypotenuse in sidec (the square root of the sums of the squares of the other sides): CREATE TABLE triangle ( sidea DOUBLE, sideb DOUBLE, sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb)) ); INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);Selecting from the table yields this result: mysql> SELECT * FROM triangle; +-------+-------+--------------------+ | sidea | sideb | sidec | +-------+-------+--------------------+ | 1 | 1 | 1.4142135623730951 | | 3 | 4 | 5 | | 6 | 8 | 10 | +-------+-------+--------------------+Any application that uses the triangle table has access to the hypotenuse values without having to specify the expression that calculates them. Generated column definitions have this syntax: col_name data_type [GENERATED ALWAYS] AS (expr) [VIRTUAL | STORED] [NOT NULL | NULL] [UNIQUE [KEY]] [[PRIMARY] KEY] [COMMENT 'string']AS (expr) indicates that the column is generated and defines the expression used to compute column values. AS may be preceded by GENERATED ALWAYS to make the generated nature of the column more explicit. Constructs that are permitted or prohibited in the expression are discussed later. The VIRTUAL or STORED keyword indicates how column values are stored, which has implications for column use:
The default is VIRTUAL if neither keyword is specified. It is permitted to mix VIRTUAL and STORED columns within a table. Other attributes may be given to indicate whether the column is indexed or can be NULL, or provide a comment. Generated column expressions must adhere to the following rules. An error occurs if an expression contains disallowed constructs.
If the expression evaluates to a data type that differs from the declared column type, implicit coercion to the declared type occurs according to the usual MySQL type-conversion rules. See Section 12.3, “Type Conversion in Expression Evaluation”. If a generated column uses the TIMESTAMP data type, the setting for explicit_defaults_for_timestamp is ignored. In such cases, if this variable is disabled then NULL is not converted to CURRENT_TIMESTAMP. In MySQL 8.0.22 and later, if the column is also declared as NOT NULL, attempting to insert NULL is explicitly rejected with ER_BAD_NULL_ERROR.
Note Expression evaluation uses the SQL mode in effect at evaluation time. If any component of the expression depends on the SQL mode, different results may occur for different uses of the table unless the SQL mode is the same during all uses. For CREATE TABLE ... LIKE, the destination table preserves generated column information from the original table. For CREATE TABLE ... SELECT, the destination table does not preserve information about whether columns in the selected-from table are generated columns. The SELECT part of the statement cannot assign values to generated columns in the destination table. Partitioning by generated columns is permitted. See Table Partitioning. A foreign key constraint on a stored generated column cannot use CASCADE, SET NULL, or SET DEFAULT as ON UPDATE referential actions, nor can it use SET NULL or SET DEFAULT as ON DELETE referential actions. A foreign key constraint on the base column of a stored generated column cannot use CASCADE, SET NULL, or SET DEFAULT as ON UPDATE or ON DELETE referential actions. A foreign key constraint cannot reference a virtual generated column. Triggers cannot use NEW.col_name or use OLD.col_name to refer to generated columns. For INSERT, REPLACE, and UPDATE, if a generated column is inserted into, replaced, or updated explicitly, the only permitted value is DEFAULT. A generated column in a view is considered updatable because it is possible to assign to it. However, if such a column is updated explicitly, the only permitted value is DEFAULT. Generated columns have several use cases, such as these:
Example: Suppose that a table t1 contains first_name and last_name columns and that applications frequently construct the full name using an expression like this: SELECT CONCAT(first_name,' ',last_name) AS full_name FROM t1;One way to avoid writing out the expression is to create a view v1 on t1, which simplifies applications by enabling them to select full_name directly without using an expression: CREATE VIEW v1 AS SELECT *, CONCAT(first_name,' ',last_name) AS full_name FROM t1; SELECT full_name FROM v1;A generated column also enables applications to select full_name directly without the need to define a view: CREATE TABLE t1 ( first_name VARCHAR(10), last_name VARCHAR(10), full_name VARCHAR(255) AS (CONCAT(first_name,' ',last_name)) ); SELECT full_name FROM t1;Page 5
13.1.20.9 Secondary Indexes and Generated ColumnsInnoDB supports secondary indexes on virtual generated columns. Other index types are not supported. A secondary index defined on a virtual column is sometimes referred to as a “virtual index”. A secondary index may be created on one or more virtual columns or on a combination of virtual columns and regular columns or stored generated columns. Secondary indexes that include virtual columns may be defined as UNIQUE. When a secondary index is created on a virtual generated column, generated column values are materialized in the records of the index. If the index is a covering index (one that includes all the columns retrieved by a query), generated column values are retrieved from materialized values in the index structure instead of computed “on the fly”. There are additional write costs to consider when using a secondary index on a virtual column due to computation performed when materializing virtual column values in secondary index records during INSERT and UPDATE operations. Even with additional write costs, secondary indexes on virtual columns may be preferable to generated stored columns, which are materialized in the clustered index, resulting in larger tables that require more disk space and memory. If a secondary index is not defined on a virtual column, there are additional costs for reads, as virtual column values must be computed each time the column's row is examined. Values of an indexed virtual column are MVCC-logged to avoid unnecessary recomputation of generated column values during rollback or during a purge operation. The data length of logged values is limited by the index key limit of 767 bytes for COMPACT and REDUNDANT row formats, and 3072 bytes for DYNAMIC and COMPRESSED row formats. Adding or dropping a secondary index on a virtual column is an in-place operation.
Indexing a Generated Column to Provide a JSON Column IndexAs noted elsewhere, JSON columns cannot be indexed directly. To create an index that references such a column indirectly, you can define a generated column that extracts the information that should be indexed, then create an index on the generated column, as shown in this example: mysql> CREATE TABLE jemp ( -> c JSON, -> g INT GENERATED ALWAYS AS (c->"$.id"), -> INDEX i (g) -> ); Query OK, 0 rows affected (0.28 sec) mysql> INSERT INTO jemp (c) VALUES > ('{"id": "1", "name": "Fred"}'), ('{"id": "2", "name": "Wilma"}'), > ('{"id": "3", "name": "Barney"}'), ('{"id": "4", "name": "Betty"}'); Query OK, 4 rows affected (0.04 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> SELECT c->>"$.name" AS name > FROM jemp WHERE g > 2; +--------+ | name | +--------+ | Barney | | Betty | +--------+ 2 rows in set (0.00 sec) mysql> EXPLAIN SELECT c->>"$.name" AS name > FROM jemp WHERE g > 2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: jemp partitions: NULL type: range possible_keys: i key: i key_len: 5 ref: NULL rows: 2 filtered: 100.00 Extra: Using where 1 row in set, 1 warning (0.00 sec) mysql> SHOW WARNINGS\G *************************** 1. row *************************** Level: Note Code: 1003 Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name')) AS `name` from `test`.`jemp` where (`test`.`jemp`.`g` > 2) 1 row in set (0.00 sec)(We have wrapped the output from the last statement in this example to fit the viewing area.) When you use EXPLAIN on a SELECT or other SQL statement containing one or more expressions that use the -> or ->> operator, these expressions are translated into their equivalents using JSON_EXTRACT() and (if needed) JSON_UNQUOTE() instead, as shown here in the output from SHOW WARNINGS immediately following this EXPLAIN statement: mysql> EXPLAIN SELECT c->>"$.name" > FROM jemp WHERE g > 2 ORDER BY c->"$.name"\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: jemp partitions: NULL type: range possible_keys: i key: i key_len: 5 ref: NULL rows: 2 filtered: 100.00 Extra: Using where; Using filesort 1 row in set, 1 warning (0.00 sec) mysql> SHOW WARNINGS\G *************************** 1. row *************************** Level: Note Code: 1003 Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name')) AS `c->>"$.name"` from `test`.`jemp` where (`test`.`jemp`.`g` > 2) order by json_extract(`test`.`jemp`.`c`,'$.name') 1 row in set (0.00 sec)See the descriptions of the -> and ->> operators, as well as those of the JSON_EXTRACT() and JSON_UNQUOTE() functions, for additional information and examples. This technique also can be used to provide indexes that indirectly reference columns of other types that cannot be indexed directly, such as GEOMETRY columns. In MySQL 8.0.21 and later, it is also possible to create an index on a JSON column using the JSON_VALUE() function with an expression that can be used to optimize queries employing the expression. See the description of that function for more information and examples. JSON columns and indirect indexing in NDB ClusterIt is also possible to use indirect indexing of JSON columns in MySQL NDB Cluster, subject to the following conditions:
The CREATE TABLE statement used to create the table jempn shown here is a version of the jemp table shown previously, with modifications making it compatible with NDB: CREATE TABLE jempn ( a BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, c JSON DEFAULT NULL, g INT GENERATED ALWAYS AS (c->"$.id") STORED, INDEX i (g) ) ENGINE=NDB;We can populate this table using the following INSERT statement: INSERT INTO jempn (c) VALUES ('{"id": "1", "name": "Fred"}'), ('{"id": "2", "name": "Wilma"}'), ('{"id": "3", "name": "Barney"}'), ('{"id": "4", "name": "Betty"}');Now NDB can use index i, as shown here: mysql> EXPLAIN SELECT c->>"$.name" AS name -> FROM jempn WHERE g > 2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: jempn partitions: p0,p1,p2,p3 type: range possible_keys: i key: i key_len: 5 ref: NULL rows: 3 filtered: 100.00 Extra: Using pushed condition (`test`.`jempn`.`g` > 2) 1 row in set, 1 warning (0.01 sec) mysql> SHOW WARNINGS\G *************************** 1. row *************************** Level: Note Code: 1003 Message: /* select#1 */ select json_unquote(json_extract(`test`.`jempn`.`c`,'$.name')) AS `name` from `test`.`jempn` where (`test`.`jempn`.`g` > 2) 1 row in set (0.00 sec)You should keep in mind that a stored generated column, as well as any index on such a column, uses DataMemory. Page 6
13.1.20.10 Invisible ColumnsMySQL supports invisible columns as of MySQL 8.0.23. An invisible column is normally hidden to queries, but can be accessed if explicitly referenced. Prior to MySQL 8.0.23, all columns are visible. As an illustration of when invisible columns may be useful, suppose that an application uses SELECT * queries to access a table, and must continue to work without modification even if the table is altered to add a new column that the application does not expect to be there. In a SELECT * query, the * evaluates to all table columns, except those that are invisible, so the solution is to add the new column as an invisible column. The column remains “hidden” from SELECT * queries, and the application continues to work as previously. A newer version of the application can refer to the invisible column if necessary by explicitly referencing it. The following sections detail how MySQL treats invisible columns.
DDL Statements and Invisible ColumnsColumns are visible by default. To explicitly specify visibility for a new column, use a VISIBLE or INVISIBLE keyword as part of the column definition for CREATE TABLE or ALTER TABLE: CREATE TABLE t1 ( i INT, j DATE INVISIBLE ) ENGINE = InnoDB; ALTER TABLE t1 ADD COLUMN k INT INVISIBLE;To alter the visibility of an existing column, use a VISIBLE or INVISIBLE keyword with one of the ALTER TABLE column-modification clauses: ALTER TABLE t1 CHANGE COLUMN j j DATE VISIBLE; ALTER TABLE t1 MODIFY COLUMN j DATE INVISIBLE; ALTER TABLE t1 ALTER COLUMN j SET VISIBLE;A table must have at least one visible column. Attempting to make all columns invisible produces an error. Invisible columns support the usual column attributes: NULL, NOT NULL, AUTO_INCREMENT, and so forth. Generated columns can be invisible. Index definitions can name invisible columns, including definitions for PRIMARY KEY and UNIQUE indexes. Although a table must have at least one visible column, an index definition need not have any visible columns. An invisible column dropped from a table is dropped in the usual way from any index definition that names the column. Foreign key constraints can be defined on invisible columns, and foreign key constraints can reference invisible columns. CHECK constraints can be defined on invisible columns. For new or modified rows, violation of a CHECK constraint on an invisible column produces an error. CREATE TABLE ... LIKE includes invisible columns, and they are invisible in the new table. CREATE TABLE ... SELECT does not include invisible columns, unless they are explicitly referenced in the SELECT part. However, even if explicitly referenced, a column that is invisible in the existing table is visible in the new table: mysql> CREATE TABLE t1 (col1 INT, col2 INT INVISIBLE); mysql> CREATE TABLE t2 AS SELECT col1, col2 FROM t1; mysql> SHOW CREATE TABLE t2\G *************************** 1. row *************************** Table: t2 Create Table: CREATE TABLE `t2` ( `col1` int DEFAULT NULL, `col2` int DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ciIf invisibility should be preserved, provide a definition for the invisible column in the CREATE TABLE part of the CREATE TABLE ... SELECT statement: mysql> CREATE TABLE t1 (col1 INT, col2 INT INVISIBLE); mysql> CREATE TABLE t2 (col2 INT INVISIBLE) AS SELECT col1, col2 FROM t1; mysql> SHOW CREATE TABLE t2\G *************************** 1. row *************************** Table: t2 Create Table: CREATE TABLE `t2` ( `col1` int DEFAULT NULL, `col2` int DEFAULT NULL /*!80023 INVISIBLE */ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ciViews can refer to invisible columns by explicitly referencing them in the SELECT statement that defines the view. Changing a column's visibility subsequent to defining a view that references the column does not change view behavior.
DML Statements and Invisible ColumnsFor SELECT statements, an invisible column is not part of the result set unless explicitly referenced in the select list. In a select list, the * and tbl_name.* shorthands do not include invisible columns. Natural joins do not include invisible columns. Consider the following statement sequence: mysql> CREATE TABLE t1 (col1 INT, col2 INT INVISIBLE); mysql> INSERT INTO t1 (col1, col2) VALUES(1, 2), (3, 4); mysql> SELECT * FROM t1; +------+ | col1 | +------+ | 1 | | 3 | +------+ mysql> SELECT col1, col2 FROM t1; +------+------+ | col1 | col2 | +------+------+ | 1 | 2 | | 3 | 4 | +------+------+The first SELECT does not reference the invisible column col2 in the select list (because * does not include invisible columns), so col2 does not appear in the statement result. The second SELECT explicitly references col2, so the column appears in the result. The statement TABLE t1 produces the same output as the first SELECT statement. Since there is no way to specify columns in a TABLE statement, TABLE never displays invisible columns. For statements that create new rows, an invisible column is assigned its implicit default value unless explicitly referenced and assigned a value. For information about implicit defaults, see Implicit Default Handling. For INSERT (and REPLACE, for non-replaced rows), implicit default assignment occurs with a missing column list, an empty column list, or a nonempty column list that does not include the invisible column: CREATE TABLE t1 (col1 INT, col2 INT INVISIBLE); INSERT INTO t1 VALUES(...); INSERT INTO t1 () VALUES(...); INSERT INTO t1 (col1) VALUES(...);For the first two INSERT statements, the VALUES() list must provide a value for each visible column and no invisible column. For the third INSERT statement, the VALUES() list must provide the same number of values as the number of named columns; the same is true when you use VALUES ROW() rather than VALUES(). For LOAD DATA and LOAD XML, implicit default assignment occurs with a missing column list or a nonempty column list that does not include the invisible column. Input rows should not include a value for the invisible column. To assign a value other than the implicit default for the preceding statements, explicitly name the invisible column in the column list and provide a value for it. INSERT INTO ... SELECT * and REPLACE INTO ... SELECT * do not include invisible columns because * does not include invisible columns. Implicit default assignment occurs as described previously. For statements that insert or ignore new rows, or that replace or modify existing rows, based on values in a PRIMARY KEY or UNIQUE index, MySQL treats invisible columns the same as visible columns: Invisible columns participate in key value comparisons. Specifically, if a new row has the same value as an existing row for a unique key value, these behaviors occur whether the index columns are visible or invisible: To update invisible columns for UPDATE statements, name them and assign a value, just as for visible columns.
Invisible Column MetadataInformation about whether a column is visible or invisible is available from the EXTRA column of the INFORMATION_SCHEMA.COLUMNS table or SHOW COLUMNS output. For example: mysql> SELECT TABLE_NAME, COLUMN_NAME, EXTRA FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 't1'; +------------+-------------+-----------+ | TABLE_NAME | COLUMN_NAME | EXTRA | +------------+-------------+-----------+ | t1 | i | | | t1 | j | | | t1 | k | INVISIBLE | +------------+-------------+-----------+Columns are visible by default, so in that case, EXTRA displays no visibility information. For invisible columns, EXTRA displays INVISIBLE. SHOW CREATE TABLE displays invisible columns in the table definition, with the INVISIBLE keyword in a version-specific comment: mysql> SHOW CREATE TABLE t1\G *************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `i` int DEFAULT NULL, `j` int DEFAULT NULL, `k` int DEFAULT NULL /*!80023 INVISIBLE */ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_cimysqldump and mysqlpump use SHOW CREATE TABLE, so they include invisible columns in dumped table definitions. They also include invisible column values in dumped data. Reloading a dump file into an older version of MySQL that does not support invisible columns causes the version-specific comment to be ignored, which creates any invisible columns as visible.
The Binary Log and Invisible ColumnsMySQL treats invisible columns as follows with respect to events in the binary log:
Page 7
13.1.20.11 Generated Invisible Primary KeysBeginning with MySQL 8.0.30, MySQL supports generated invisible primary keys when running in GIPK mode. When running in this mode, for any InnoDB table that is created without an explicit primary key, the MySQL server automatically adds a generated invisible primary key (GIPK) to the table. This section describes how to enable and disable GIPK mode, and the behavior of CREATE TABLE and of the generated primary key when this mode is enabled. GIPK mode is controlled by the sql_generate_invisible_primary_key server system variable. By default, the value of this variable is OFF, which means that GIPK mode is disabled; to enable GIPK mode, set the variable to ON. To illustrate how table creation is affected by GIPK mode, we begin by creating two identical tables, neither having a primary key, the first (table auto_0) with GIPK mode disabled, and the second (auto_1) after enabling it, as shown here: mysql> SELECT @@sql_generate_invisible_primary_key; +--------------------------------------+ | @@sql_generate_invisible_primary_key | +--------------------------------------+ | 0 | +--------------------------------------+ 1 row in set (0.00 sec) mysql> CREATE TABLE auto_0 (c1 VARCHAR(50), c2 INT); Query OK, 0 rows affected (0.02 sec) mysql> SET sql_generate_invisible_primary_key=ON; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@sql_generate_invisible_primary_key; +--------------------------------------+ | @@sql_generate_invisible_primary_key | +--------------------------------------+ | 1 | +--------------------------------------+ 1 row in set (0.00 sec) mysql> CREATE TABLE auto_1 (c1 VARCHAR(50), c2 INT); Query OK, 0 rows affected (0.04 sec)The setting for sql_generate_invisible_primary_key is not replicated, and is ignored by replication applier threads. This means that the replica does not generate a primary key for any replicated table which was not created with a primary key on the source. Use SHOW CREATE TABLE to see the difference in how the tables were actually created, like this: mysql> SHOW CREATE TABLE auto_0\G *************************** 1. row *************************** Table: auto_0 Create Table: CREATE TABLE `auto_0` ( `c1` varchar(50) DEFAULT NULL, `c2` int DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) mysql> SHOW CREATE TABLE auto_1\G *************************** 1. row *************************** Table: auto_1 Create Table: CREATE TABLE `auto_1` ( `my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */, `c1` varchar(50) DEFAULT NULL, `c2` int DEFAULT NULL, PRIMARY KEY (`my_row_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)Since auto_1 had no primary key specified by the CREATE TABLE statement used to create it, GIPK mode causes MySQL to add to this table the invisible column my_row_id and a primary key on that column. Since GIPK mode was disabled when auto_0 was created, no such addition was performed on that table. When a primary key is added to a table by the server in GIPK mode, the column and key name is always my_row_id. For this reason, when GIPK mode is enabled, you cannot create a table having a column named my_row_id unless the table creation statement also specifies an explicit primary key. (You are not required to name the column or key my_row_id in such cases.) my_row_id is an invisible column, which means it is not shown in the output of SELECT * or TABLE; the column must be selected explicitly, by name (see Section 13.1.20.10, “Invisible Columns”). When GIPK mode is in effect, the generated primary key cannot be altered other than to switch it between VISIBLE and INVISIBLE. To make the generated invisible primary key on auto_1 visible, execute this ALTER TABLE statement: mysql> ALTER TABLE auto_1 ALTER COLUMN my_row_id SET VISIBLE; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW CREATE TABLE auto_1\G *************************** 1. row *************************** Table: auto_1 Create Table: CREATE TABLE `auto_1` ( `my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT, `c1` varchar(50) DEFAULT NULL, `c2` int DEFAULT NULL, PRIMARY KEY (`my_row_id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.01 sec)To make this generated primary key invisible again, issue ALTER TABLE auto_1 ALTER COLUMN my_row_id SET INVISIBLE. A primary key generated by GIPK mode is always invisible by default. Whenever GIPK mode is in effect, you cannot drop a generated primary key if either of the following 2 conditions would result:
GIPK mode applies only to tables using the InnoDB storage engine. It has no effect on tables using other MySQL storage engines. You can use an ALTER TABLE statement to change the storage engine used by a table that has a generated invisible primary key; the primary key and column remain in place, but the table and key no longer receive any special treatment. By default, GIPKs are shown in the output of SHOW CREATE TABLE, SHOW COLUMNS, and SHOW INDEX, and are visible in the Information Schema COLUMNS and STATISTICS tables. You can cause generated invisible primary keys to be hidden instead in such cases by setting the show_gipk_in_create_table_and_information_schema system variable to OFF. By default, this variable is ON, as shown here: mysql> SELECT @@show_gipk_in_create_table_and_information_schema; +----------------------------------------------------+ | @@show_gipk_in_create_table_and_information_schema | +----------------------------------------------------+ | 1 | +----------------------------------------------------+ 1 row in set (0.00 sec)As can be seen from the following query against the COLUMNS table, my_row_id is visible among the columns of auto_1: mysql> SELECT COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, COLUMN_KEY -> FROM INFORMATION_SCHEMA.COLUMNS -> WHERE TABLE_NAME = "auto_1"; +-------------+------------------+-----------+------------+ | COLUMN_NAME | ORDINAL_POSITION | DATA_TYPE | COLUMN_KEY | +-------------+------------------+-----------+------------+ | my_row_id | 1 | bigint | PRI | | c1 | 2 | varchar | | | c2 | 3 | int | | +-------------+------------------+-----------+------------+ 3 rows in set (0.01 sec)After show_gipk_in_create_table_and_information_schema is set to OFF, my_row_id can no longer be seen in the COLUMNS table, as shown here: mysql> SET show_gipk_in_create_table_and_information_schema = OFF; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@show_gipk_in_create_table_and_information_schema; +----------------------------------------------------+ | @@show_gipk_in_create_table_and_information_schema | +----------------------------------------------------+ | 0 | +----------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, COLUMN_KEY -> FROM INFORMATION_SCHEMA.COLUMNS -> WHERE TABLE_NAME = "auto_1"; +-------------+------------------+-----------+------------+ | COLUMN_NAME | ORDINAL_POSITION | DATA_TYPE | COLUMN_KEY | +-------------+------------------+-----------+------------+ | c1 | 2 | varchar | | | c2 | 3 | int | | +-------------+------------------+-----------+------------+ 2 rows in set (0.00 sec)GIPK mode supports row-based replication of CREATE TABLE ... SELECT; the information written to the binary log for this statement in such cases includes the GIPK definition, and thus is replicated correctly. Statement-based replication of CREATE TABLE ... SELECT is not supported in GIPK mode. When creating or importing backups of installations in which GIPK mode is in use, it is possible to exclude generated invisible PK columns and values. The --skip-generated-invisible-primary-key option for mysqldump causes GIPK information to be excluded in the program's output. If you are importing a dump file that contains GIPK keys and values, you can also use --skip-generated-invisible-primary-key with mysqlpump to cause these to be suppressed (and thus not imported). Page 8
13.1.20.12 Setting NDB Comment Options
It is possible to set a number of options specific to NDB Cluster in the table comment or column comments of an NDB table. Table-level options for controlling read from any replica and partition balance can be embedded in a table comment using NDB_TABLE. NDB_COLUMN can be used in a column comment to set the size of the blob parts table column used for storing parts of blob values by NDB to its maximum. This works for BLOB, MEDIUMBLOB, LONGBLOB, TEXT, MEDIUMTEXT, LONGTEXT, and JSON columns. Beginning with NDB 8.0.30, a column comment can also be used to control the inline size of a blob column. NDB_COLUMN comments do not support TINYBLOB or TINYTEXT columns, since these have an inline part (only) of fixed size, and no separate parts to store elsewhere. NDB_TABLE can be used in a table comment to set options relating to partition balance and whether the table is fully replicated, among others. The remainder of this section describes these options and their use. NDB_COLUMN OptionsIn NDB Cluster, a column comment in a CREATE TABLE or ALTER TABLE statement can also be used to specify an NDB_COLUMN option. Beginning with version 8.0.30, NDB supports two column comment options BLOB_INLINE_SIZE and MAX_BLOB_PART_SIZE. (Prior to NDB 8.0.30, only MAX_BLOB_PART_SIZE is supported.) Syntax for this option is shown here: COMMENT 'NDB_COLUMN=speclist' speclist := spec[,spec] spec := BLOB_INLINE_SIZE=value | MAX_BLOB_PART_SIZE[={0|1}]BLOB_INLINE_SIZE specifies the number of bytes to be stored inline by the column; its expected value is an integer in the range 1 - 29980, inclusive. Setting a value greater than 29980 raises an error; setting a value less than 1 is allowed, but causes the default inline size for the column type to be used. You should be aware that the maximum value for this option is actually the maximum number of bytes that can be stored in one row of an NDB table; every column in the row contributes to this total. You should also keep in mind, especially when working with TEXT columns, that the value set by MAX_BLOB_PART_SIZE or BLOB_INLINE_SIZE represents column size in bytes. It does not indicate the number of characters, which varies according to the character set and collation used by the column. To see the effects of this option, first create a table with two BLOB columns, one (b1) with no extra options, and another (b2) with a setting for BLOB_INLINE_SIZE, as shown here: mysql> CREATE TABLE t1 ( -> a INT NOT NULL PRIMARY KEY, -> b1 BLOB, -> b2 BLOB COMMENT 'NDB_COLUMN=BLOB_INLINE_SIZE=8000' -> ) ENGINE NDB; Query OK, 0 rows affected (0.32 sec)You can see the BLOB_INLINE_SIZE settings for the BLOB columns by querying the ndbinfo.blobs table, like this: mysql> SELECT -> column_name AS 'Column Name', -> inline_size AS 'Inline Size', -> part_size AS 'Blob Part Size' -> FROM ndbinfo.blobs -> WHERE table_name = 't1'; +-------------+-------------+----------------+ | Column Name | Inline Size | Blob Part Size | +-------------+-------------+----------------+ | b1 | 256 | 2000 | | b2 | 8000 | 2000 | +-------------+-------------+----------------+ 2 rows in set (0.01 sec)You can also check the output from the ndb_desc utility, as shown here, with the relevant lines displayed using emphasized text: $> ndb_desc -d test t1 -- t -- Version: 1 Fragment type: HashMapPartition K Value: 6 Min load factor: 78 Max load factor: 80 Temporary table: no Number of attributes: 3 Number of primary keys: 1 Length of frm data: 945 Max Rows: 0 Row Checksum: 1 Row GCI: 1 SingleUserMode: 0 ForceVarPart: 1 PartitionCount: 2 FragmentCount: 2 PartitionBalance: FOR_RP_BY_LDM ExtraRowGciBits: 0 ExtraRowAuthorBits: 0 TableStatus: Retrieved Table options: readbackup HashMap: DEFAULT-HASHMAP-3840-2 -- Attributes -- a Int PRIMARY KEY DISTRIBUTION KEY AT=FIXED ST=MEMORY b1 Blob(256,2000,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_64_1 b2 Blob(8000,2000,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_64_2 -- Indexes -- PRIMARY KEY(a) - UniqueHashIndex PRIMARY(a) - OrderedIndex NDBT_ProgramExit: 0 - OKFor MAX_BLOB_PART_SIZE, the = sign and the value following it are optional. Using any value other than 0 or 1 results in a syntax error. The effect of using MAX_BLOB_PART_SIZE in a column comment is to set the blob part size of a TEXT or BLOB column to the maximum number of bytes supported for this by NDB (13948). This option can be applied to any blob column type supported by MySQL except TINYBLOB or TINYTEXT (BLOB, MEDIUMBLOB, LONGBLOB, TEXT, MEDIUMTEXT, LONGTEXT). Unlike BLOB_INLINE_SIZE, MAX_BLOB_PART_SIZE has no effect on JSON columns. To see the effects of this option, we first run the following SQL statement in the mysql client to create a table with two BLOB columns, one (c1) with no extra options, and another (c2) with MAX_BLOB_PART_SIZE: mysql> CREATE TABLE test.t2 ( -> p INT PRIMARY KEY, -> c1 BLOB, -> c2 BLOB COMMENT 'NDB_COLUMN=MAX_BLOB_PART_SIZE' -> ) ENGINE NDB; Query OK, 0 rows affected (0.32 sec)From the system shell, run the ndb_desc utility to obtain information about the table just created, as shown in this example: $> ndb_desc -d test t2 -- t -- Version: 1 Fragment type: HashMapPartition K Value: 6 Min load factor: 78 Max load factor: 80 Temporary table: no Number of attributes: 3 Number of primary keys: 1 Length of frm data: 324 Row Checksum: 1 Row GCI: 1 SingleUserMode: 0 ForceVarPart: 1 FragmentCount: 2 ExtraRowGciBits: 0 ExtraRowAuthorBits: 0 TableStatus: Retrieved HashMap: DEFAULT-HASHMAP-3840-2 -- Attributes -- p Int PRIMARY KEY DISTRIBUTION KEY AT=FIXED ST=MEMORY c1 Blob(256,2000,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_22_1 c2 Blob(256,13948,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_22_2 -- Indexes -- PRIMARY KEY(p) - UniqueHashIndex PRIMARY(p) - OrderedIndexColumn information in the output is listed under Attributes; for columns c1 and c2 it is displayed here in emphasized text. For c1, the blob part size is 2000, the default value; for c2, it is 13948, as set by MAX_BLOB_PART_SIZE. You can also query the ndbinfo.blobs table to see this, as shown here: mysql> SELECT -> column_name AS 'Column Name', -> inline_size AS 'Inline Size', -> part_size AS 'Blob Part Size' -> FROM ndbinfo.blobs -> WHERE table_name = 't2'; +-------------+-------------+----------------+ | Column Name | Inline Size | Blob Part Size | +-------------+-------------+----------------+ | c1 | 256 | 2000 | | c2 | 256 | 13948 | +-------------+-------------+----------------+ 2 rows in set (0.00 sec)You can change the blob part size for a given blob column of an NDB table using an ALTER TABLE statement such as this one, and verifying the changes afterwards using SHOW CREATE TABLE: mysql> ALTER TABLE test.t2 -> DROP COLUMN c1, -> ADD COLUMN c1 BLOB COMMENT 'NDB_COLUMN=MAX_BLOB_PART_SIZE', -> CHANGE COLUMN c2 c2 BLOB AFTER c1; Query OK, 0 rows affected (0.47 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW CREATE TABLE test.t2\G *************************** 1. row *************************** Table: t Create Table: CREATE TABLE `t2` ( `p` int(11) NOT NULL, `c1` blob COMMENT 'NDB_COLUMN=MAX_BLOB_PART_SIZE', `c2` blob, PRIMARY KEY (`p`) ) ENGINE=ndbcluster DEFAULT CHARSET=latin1 1 row in set (0.00 sec) mysql> EXIT ByeThe output of ndb_desc shows that the blob part sizes of the columns have been changed as expected: $> ndb_desc -d test t2 -- t -- Version: 16777220 Fragment type: HashMapPartition K Value: 6 Min load factor: 78 Max load factor: 80 Temporary table: no Number of attributes: 3 Number of primary keys: 1 Length of frm data: 324 Row Checksum: 1 Row GCI: 1 SingleUserMode: 0 ForceVarPart: 1 FragmentCount: 2 ExtraRowGciBits: 0 ExtraRowAuthorBits: 0 TableStatus: Retrieved HashMap: DEFAULT-HASHMAP-3840-2 -- Attributes -- p Int PRIMARY KEY DISTRIBUTION KEY AT=FIXED ST=MEMORY c1 Blob(256,13948,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_26_1 c2 Blob(256,2000,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_26_2 -- Indexes -- PRIMARY KEY(p) - UniqueHashIndex PRIMARY(p) - OrderedIndex NDBT_ProgramExit: 0 - OKYou can also see the change by running the query against ndbinfo.blobs again: mysql> SELECT -> column_name AS 'Column Name', -> inline_size AS 'Inline Size', -> part_size AS 'Blob Part Size' -> FROM ndbinfo.blobs -> WHERE table_name = 't2'; +-------------+-------------+----------------+ | Column Name | Inline Size | Blob Part Size | +-------------+-------------+----------------+ | c1 | 256 | 13948 | | c2 | 256 | 2000 | +-------------+-------------+----------------+ 2 rows in set (0.00 sec)It is possible to set both BLOB_INLINE_SIZE and MAX_BLOB_PART_SIZE for a blob column, as shown in this CREATE TABLE statement: mysql> CREATE TABLE test.t3 ( -> p INT NOT NULL PRIMARY KEY, -> c1 JSON, -> c2 JSON COMMENT 'NDB_COLUMN=BLOB_INLINE_SIZE=5000,MAX_BLOB_PART_SIZE' -> ) ENGINE NDB; Query OK, 0 rows affected (0.28 sec)Querying the blobs table shows us that the statement worked as expected: mysql> SELECT -> column_name AS 'Column Name', -> inline_size AS 'Inline Size', -> part_size AS 'Blob Part Size' -> FROM ndbinfo.blobs -> WHERE table_name = 't3'; +-------------+-------------+----------------+ | Column Name | Inline Size | Blob Part Size | +-------------+-------------+----------------+ | c1 | 4000 | 8100 | | c2 | 5000 | 8100 | +-------------+-------------+----------------+ 2 rows in set (0.00 sec)You can also verify that the statement worked by checking the output of ndb_desc. Changing a column's blob part size must be done using a copying ALTER TABLE; this operation cannot be performed online (see Section 23.6.12, “Online Operations with ALTER TABLE in NDB Cluster”). For more information about how NDB stores columns of blob types, see String Type Storage Requirements. NDB_TABLE OptionsIn MySQL NDB Cluster, the table comment in a CREATE TABLE or ALTER TABLE statement can also be used to specify an NDB_TABLE option, which consists of one or more name-value pairs, separated by commas if need be, following the string NDB_TABLE=. Complete syntax for names and values syntax is shown here: COMMENT="NDB_TABLE=ndb_table_option[,ndb_table_option[,...]]" ndb_table_option: { NOLOGGING={1 | 0} | READ_BACKUP={1 | 0} | PARTITION_BALANCE={FOR_RP_BY_NODE | FOR_RA_BY_NODE | FOR_RP_BY_LDM | FOR_RA_BY_LDM | FOR_RA_BY_LDM_X_2 | FOR_RA_BY_LDM_X_3 | FOR_RA_BY_LDM_X_4} | FULLY_REPLICATED={1 | 0} }Spaces are not permitted within the quoted string. The string is case-insensitive. The four NDB table options that can be set as part of a comment in this way are described in more detail in the next few paragraphs. NOLOGGING: Using 1 corresponds to having ndb_table_no_logging enabled, but has no actual effect. Provided as a placeholder, mostly for completeness of ALTER TABLE statements. READ_BACKUP: Setting this option to 1 has the same effect as though ndb_read_backup were enabled; enables reading from any replica. Doing so greatly improves the performance of reads from the table at a relatively small cost to write performance. Beginning with NDB 8.0.19, 1 is the default for READ_BACKUP, and the default for ndb_read_backup is ON (previously, read from any replica was disabled by default). You can set READ_BACKUP for an existing table online, using an ALTER TABLE statement similar to one of those shown here: ALTER TABLE ... ALGORITHM=INPLACE, COMMENT="NDB_TABLE=READ_BACKUP=1"; ALTER TABLE ... ALGORITHM=INPLACE, COMMENT="NDB_TABLE=READ_BACKUP=0";For more information about the ALGORITHM option for ALTER TABLE, see Section 23.6.12, “Online Operations with ALTER TABLE in NDB Cluster”. PARTITION_BALANCE: Provides additional control over assignment and placement of partitions. The following four schemes are supported:
PARTITION_BALANCE is the preferred interface for setting the number of partitions per table. Using MAX_ROWS to force the number of partitions is deprecated but continues to be supported for backward compatibility; it is subject to removal in a future release of MySQL NDB Cluster. (Bug #81759, Bug #23544301) FULLY_REPLICATED controls whether the table is fully replicated, that is, whether each data node has a complete copy of the table. To enable full replication of the table, use FULLY_REPLICATED=1. This setting can also be controlled using the ndb_fully_replicated system variable. Setting it to ON enables the option by default for all new NDB tables; the default is OFF. The ndb_data_node_neighbour system variable is also used for fully replicated tables, to ensure that when a fully replicated table is accessed, we access the data node which is local to this MySQL Server. An example of a CREATE TABLE statement using such a comment when creating an NDB table is shown here: mysql> CREATE TABLE t1 ( > c1 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, > c2 VARCHAR(100), > c3 VARCHAR(100) ) > ENGINE=NDB > COMMENT="NDB_TABLE=READ_BACKUP=0,PARTITION_BALANCE=FOR_RP_BY_NODE";The comment is displayed as part of the output of SHOW CREATE TABLE. The text of the comment is also available from querying the MySQL Information Schema TABLES table, as in this example: mysql> SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_COMMENT > FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME="t1"\G *************************** 1. row *************************** TABLE_NAME: t1 TABLE_SCHEMA: test TABLE_COMMENT: NDB_TABLE=READ_BACKUP=0,PARTITION_BALANCE=FOR_RP_BY_NODE 1 row in set (0.01 sec)This comment syntax is also supported with ALTER TABLE statements for NDB tables, as shown here: mysql> ALTER TABLE t1 COMMENT="NDB_TABLE=PARTITION_BALANCE=FOR_RA_BY_NODE"; Query OK, 0 rows affected (0.40 sec) Records: 0 Duplicates: 0 Warnings: 0Beginning with NDB 8.0.21, the TABLE_COMMENT column displays the comment that is required to re-create the table as it is following the ALTER TABLE statement, like this: mysql> SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_COMMENT -> FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME="t1"\G *************************** 1. row *************************** TABLE_NAME: t1 TABLE_SCHEMA: test TABLE_COMMENT: NDB_TABLE=READ_BACKUP=0,PARTITION_BALANCE=FOR_RP_BY_NODE 1 row in set (0.01 sec)mysql> SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_COMMENT > FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME="t1"; +------------+--------------+--------------------------------------------------+ | TABLE_NAME | TABLE_SCHEMA | TABLE_COMMENT | +------------+--------------+--------------------------------------------------+ | t1 | c | NDB_TABLE=PARTITION_BALANCE=FOR_RA_BY_NODE | | t1 | d | | +------------+--------------+--------------------------------------------------+ 2 rows in set (0.01 sec)Keep in mind that a table comment used with ALTER TABLE replaces any existing comment which the table might have. mysql> ALTER TABLE t1 COMMENT="NDB_TABLE=PARTITION_BALANCE=FOR_RA_BY_NODE"; Query OK, 0 rows affected (0.40 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_COMMENT > FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME="t1"; +------------+--------------+--------------------------------------------------+ | TABLE_NAME | TABLE_SCHEMA | TABLE_COMMENT | +------------+--------------+--------------------------------------------------+ | t1 | c | NDB_TABLE=PARTITION_BALANCE=FOR_RA_BY_NODE | | t1 | d | | +------------+--------------+--------------------------------------------------+ 2 rows in set (0.01 sec)Prior to NDB 8.0.21, the table comment used with ALTER TABLE replaced any existing comment which the table might have had. This meant that (for example) the READ_BACKUP value was not carried over to the new comment set by the ALTER TABLE statement, and that any unspecified values reverted to their defaults. (BUG#30428829) There was thus no longer any way using SQL to retrieve the value previously set for the comment. To keep comment values from reverting to their defaults, it was necessary to preserve any such values from the existing comment string and include them in the comment passed to ALTER TABLE. You can also see the value of the PARTITION_BALANCE option in the output of ndb_desc. ndb_desc also shows whether the READ_BACKUP and FULLY_REPLICATED options are set for the table. See the description of this program for more information. Page 9
13.1.21 CREATE TABLESPACE StatementCREATE [UNDO] TABLESPACE tablespace_name InnoDB and NDB: [ADD DATAFILE 'file_name'] [AUTOEXTEND_SIZE [=] value] InnoDB only: [FILE_BLOCK_SIZE = value] [ENCRYPTION [=] {'Y' | 'N'}] NDB only: USE LOGFILE GROUP logfile_group [EXTENT_SIZE [=] extent_size] [INITIAL_SIZE [=] initial_size] [MAX_SIZE [=] max_size] [NODEGROUP [=] nodegroup_id] [WAIT] [COMMENT [=] 'string'] InnoDB and NDB: [ENGINE [=] engine_name] Reserved for future use: [ENGINE_ATTRIBUTE [=] 'string']This statement is used to create a tablespace. The precise syntax and semantics depend on the storage engine used. In standard MySQL releases, this is always an InnoDB tablespace. MySQL NDB Cluster also supports tablespaces using the NDB storage engine. CREATE TABLESPACE syntax is used to create general tablespaces or undo tablespaces. The UNDO keyword, introduced in MySQL 8.0.14, must be specified to create an undo tablespace. A general tablespace is a shared tablespace. It can hold multiple tables, and supports all table row formats. General tablespaces can be created in a location relative to or independent of the data directory. After creating an InnoDB general tablespace, use CREATE TABLE tbl_name ... TABLESPACE [=] tablespace_name or ALTER TABLE tbl_name TABLESPACE [=] tablespace_name to add tables to the tablespace. For more information, see Section 15.6.3.3, “General Tablespaces”. Undo tablespaces contain undo logs. Undo tablespaces can be created in a chosen location by specifying a fully qualified data file path. For more information, see Section 15.6.3.4, “Undo Tablespaces”. Considerations for NDB ClusterThis statement is used to create a tablespace, which can contain one or more data files, providing storage space for NDB Cluster Disk Data tables (see Section 23.6.11, “NDB Cluster Disk Data Tables”). One data file is created and added to the tablespace using this statement. Additional data files may be added to the tablespace by using the ALTER TABLESPACE statement (see Section 13.1.10, “ALTER TABLESPACE Statement”).
Note All NDB Cluster Disk Data objects share the same namespace. This means that each Disk Data object must be uniquely named (and not merely each Disk Data object of a given type). For example, you cannot have a tablespace and a log file group with the same name, or a tablespace and a data file with the same name. A log file group of one or more UNDO log files must be assigned to the tablespace to be created with the USE LOGFILE GROUP clause. logfile_group must be an existing log file group created with CREATE LOGFILE GROUP (see Section 13.1.16, “CREATE LOGFILE GROUP Statement”). Multiple tablespaces may use the same log file group for UNDO logging. When setting EXTENT_SIZE or INITIAL_SIZE, you may optionally follow the number with a one-letter abbreviation for an order of magnitude, similar to those used in my.cnf. Generally, this is one of the letters M (for megabytes) or G (for gigabytes). INITIAL_SIZE and EXTENT_SIZE are subject to rounding as follows:
Note NDB reserves 4% of a tablespace for data node restart operations. This reserved space cannot be used for data storage. The rounding just described is done explicitly, and a warning is issued by the MySQL Server when any such rounding is performed. The rounded values are also used by the NDB kernel for calculating INFORMATION_SCHEMA.FILES column values and other purposes. However, to avoid an unexpected result, we suggest that you always use whole multiples of 32K in specifying these options. When CREATE TABLESPACE is used with ENGINE [=] NDB, a tablespace and associated data file are created on each Cluster data node. You can verify that the data files were created and obtain information about them by querying the INFORMATION_SCHEMA.FILES table. (See the example later in this section.) (See Section 26.3.15, “The INFORMATION_SCHEMA FILES Table”.) Options
This example demonstrates creating a general tablespace and adding three uncompressed tables of different row formats. mysql> CREATE TABLESPACE `ts1` ADD DATAFILE 'ts1.ibd' ENGINE=INNODB; mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1 ROW_FORMAT=REDUNDANT; mysql> CREATE TABLE t2 (c1 INT PRIMARY KEY) TABLESPACE ts1 ROW_FORMAT=COMPACT; mysql> CREATE TABLE t3 (c1 INT PRIMARY KEY) TABLESPACE ts1 ROW_FORMAT=DYNAMIC;This example demonstrates creating a general tablespace and adding a compressed table. The example assumes a default innodb_page_size value of 16K. The FILE_BLOCK_SIZE of 8192 requires that the compressed table have a KEY_BLOCK_SIZE of 8. mysql> CREATE TABLESPACE `ts2` ADD DATAFILE 'ts2.ibd' FILE_BLOCK_SIZE = 8192 Engine=InnoDB; mysql> CREATE TABLE t4 (c1 INT PRIMARY KEY) TABLESPACE ts2 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;This example demonstrates creating a general tablespace without specifying the ADD DATAFILE clause, which is optional as of MySQL 8.0.14. mysql> CREATE TABLESPACE `ts3` ENGINE=INNODB;This example demonstrates creating an undo tablespace. mysql> CREATE UNDO TABLESPACE undo_003 ADD DATAFILE 'undo_003.ibu';NDB ExampleSuppose that you wish to create an NDB Cluster Disk Data tablespace named myts using a datafile named mydata-1.dat. An NDB tablespace always requires the use of a log file group consisting of one or more undo log files. For this example, we first create a log file group named mylg that contains one undo long file named myundo-1.dat, using the CREATE LOGFILE GROUP statement shown here: mysql> CREATE LOGFILE GROUP myg1 -> ADD UNDOFILE 'myundo-1.dat' -> ENGINE=NDB; Query OK, 0 rows affected (3.29 sec)Now you can create the tablespace previously described using the following statement: mysql> CREATE TABLESPACE myts -> ADD DATAFILE 'mydata-1.dat' -> USE LOGFILE GROUP mylg -> ENGINE=NDB; Query OK, 0 rows affected (2.98 sec)You can now create a Disk Data table using a CREATE TABLE statement with the TABLESPACE and STORAGE DISK options, similar to what is shown here: mysql> CREATE TABLE mytable ( -> id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, -> lname VARCHAR(50) NOT NULL, -> fname VARCHAR(50) NOT NULL, -> dob DATE NOT NULL, -> joined DATE NOT NULL, -> INDEX(last_name, first_name) -> ) -> TABLESPACE myts STORAGE DISK -> ENGINE=NDB; Query OK, 0 rows affected (1.41 sec)It is important to note that only the dob and joined columns from mytable are actually stored on disk, due to the fact that the id, lname, and fname columns are all indexed. As mentioned previously, when CREATE TABLESPACE is used with ENGINE [=] NDB, a tablespace and associated data file are created on each NDB Cluster data node. You can verify that the data files were created and obtain information about them by querying the INFORMATION_SCHEMA.FILES table, as shown here: mysql> SELECT FILE_NAME, FILE_TYPE, LOGFILE_GROUP_NAME, STATUS, EXTRA -> FROM INFORMATION_SCHEMA.FILES -> WHERE TABLESPACE_NAME = 'myts'; +--------------+------------+--------------------+--------+----------------+ | file_name | file_type | logfile_group_name | status | extra | +--------------+------------+--------------------+--------+----------------+ | mydata-1.dat | DATAFILE | mylg | NORMAL | CLUSTER_NODE=5 | | mydata-1.dat | DATAFILE | mylg | NORMAL | CLUSTER_NODE=6 | | NULL | TABLESPACE | mylg | NORMAL | NULL | +--------------+------------+--------------------+--------+----------------+ 3 rows in set (0.01 sec)For additional information and examples, see Section 23.6.11.1, “NDB Cluster Disk Data Objects”. Page 10
13.1.22 CREATE TRIGGER StatementCREATE [DEFINER = user] TRIGGER [IF NOT EXISTS] trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW [trigger_order] trigger_body trigger_time: { BEFORE | AFTER } trigger_event: { INSERT | UPDATE | DELETE } trigger_order: { FOLLOWS | PRECEDES } other_trigger_nameThis statement creates a new trigger. A trigger is a named database object that is associated with a table, and that activates when a particular event occurs for the table. The trigger becomes associated with the table named tbl_name, which must refer to a permanent table. You cannot associate a trigger with a TEMPORARY table or a view. Trigger names exist in the schema namespace, meaning that all triggers must have unique names within a schema. Triggers in different schemas can have the same name. IF NOT EXISTS prevents an error from occurring if a trigger having the same name, on the same table, exists in the same schema. This option is supported with CREATE TRIGGER beginning with MySQL 8.0.29. This section describes CREATE TRIGGER syntax. For additional discussion, see Section 25.3.1, “Trigger Syntax and Examples”. CREATE TRIGGER requires the TRIGGER privilege for the table associated with the trigger. If the DEFINER clause is present, the privileges required depend on the user value, as discussed in Section 25.6, “Stored Object Access Control”. If binary logging is enabled, CREATE TRIGGER might require the SUPER privilege, as discussed in Section 25.7, “Stored Program Binary Logging”. The DEFINER clause determines the security context to be used when checking access privileges at trigger activation time, as described later in this section. trigger_time is the trigger action time. It can be BEFORE or AFTER to indicate that the trigger activates before or after each row to be modified. Basic column value checks occur prior to trigger activation, so you cannot use BEFORE triggers to convert values inappropriate for the column type to valid values. trigger_event indicates the kind of operation that activates the trigger. These trigger_event values are permitted:
The trigger_event does not represent a literal type of SQL statement that activates the trigger so much as it represents a type of table operation. For example, an INSERT trigger activates not only for INSERT statements but also LOAD DATA statements because both statements insert rows into a table. A potentially confusing example of this is the INSERT INTO ... ON DUPLICATE KEY UPDATE ... syntax: a BEFORE INSERT trigger activates for every row, followed by either an AFTER INSERT trigger or both the BEFORE UPDATE and AFTER UPDATE triggers, depending on whether there was a duplicate key for the row.
Note Cascaded foreign key actions do not activate triggers. It is possible to define multiple triggers for a given table that have the same trigger event and action time. For example, you can have two BEFORE UPDATE triggers for a table. By default, triggers that have the same trigger event and action time activate in the order they were created. To affect trigger order, specify a trigger_order clause that indicates FOLLOWS or PRECEDES and the name of an existing trigger that also has the same trigger event and action time. With FOLLOWS, the new trigger activates after the existing trigger. With PRECEDES, the new trigger activates before the existing trigger. trigger_body is the statement to execute when the trigger activates. To execute multiple statements, use the BEGIN ... END compound statement construct. This also enables you to use the same statements that are permitted within stored routines. See Section 13.6.1, “BEGIN ... END Compound Statement”. Some statements are not permitted in triggers; see Section 25.8, “Restrictions on Stored Programs”. Within the trigger body, you can refer to columns in the subject table (the table associated with the trigger) by using the aliases OLD and NEW. OLD.col_name refers to a column of an existing row before it is updated or deleted. NEW.col_name refers to the column of a new row to be inserted or an existing row after it is updated. Triggers cannot use NEW.col_name or use OLD.col_name to refer to generated columns. For information about generated columns, see Section 13.1.20.8, “CREATE TABLE and Generated Columns”. MySQL stores the sql_mode system variable setting in effect when a trigger is created, and always executes the trigger body with this setting in force, regardless of the current server SQL mode when the trigger begins executing. The DEFINER clause specifies the MySQL account to be used when checking access privileges at trigger activation time. If the DEFINER clause is present, the user value should be a MySQL account specified as 'user_name'@'host_name', CURRENT_USER, or CURRENT_USER(). The permitted user values depend on the privileges you hold, as discussed in Section 25.6, “Stored Object Access Control”. Also see that section for additional information about trigger security. If the DEFINER clause is omitted, the default definer is the user who executes the CREATE TRIGGER statement. This is the same as specifying DEFINER = CURRENT_USER explicitly. MySQL takes the DEFINER user into account when checking trigger privileges as follows:
Within a trigger body, the CURRENT_USER function returns the account used to check privileges at trigger activation time. This is the DEFINER user, not the user whose actions caused the trigger to be activated. For information about user auditing within triggers, see Section 6.2.23, “SQL-Based Account Activity Auditing”. If you use LOCK TABLES to lock a table that has triggers, the tables used within the trigger are also locked, as described in LOCK TABLES and Triggers. For additional discussion of trigger use, see Section 25.3.1, “Trigger Syntax and Examples”. Page 11
13.1.23 CREATE VIEW StatementCREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] [DEFINER = user] [SQL SECURITY { DEFINER | INVOKER }] VIEW view_name [(column_list)] AS select_statement [WITH [CASCADED | LOCAL] CHECK OPTION]The CREATE VIEW statement creates a new view, or replaces an existing view if the OR REPLACE clause is given. If the view does not exist, CREATE OR REPLACE VIEW is the same as CREATE VIEW. If the view does exist, CREATE OR REPLACE VIEW replaces it. For information about restrictions on view use, see Section 25.9, “Restrictions on Views”. The select_statement is a SELECT statement that provides the definition of the view. (Selecting from the view selects, in effect, using the SELECT statement.) The select_statement can select from base tables, other views. Beginning with MySQL 8.0.19, the SELECT statement can use a VALUES statement as its source, or can be replaced with a TABLE statement, as with CREATE TABLE ... SELECT. The view definition is “frozen” at creation time and is not affected by subsequent changes to the definitions of the underlying tables. For example, if a view is defined as SELECT * on a table, new columns added to the table later do not become part of the view, and columns dropped from the table result in an error when selecting from the view. The ALGORITHM clause affects how MySQL processes the view. The DEFINER and SQL SECURITY clauses specify the security context to be used when checking access privileges at view invocation time. The WITH CHECK OPTION clause can be given to constrain inserts or updates to rows in tables referenced by the view. These clauses are described later in this section. The CREATE VIEW statement requires the CREATE VIEW privilege for the view, and some privilege for each column selected by the SELECT statement. For columns used elsewhere in the SELECT statement, you must have the SELECT privilege. If the OR REPLACE clause is present, you must also have the DROP privilege for the view. If the DEFINER clause is present, the privileges required depend on the user value, as discussed in Section 25.6, “Stored Object Access Control”. When a view is referenced, privilege checking occurs as described later in this section. A view belongs to a database. By default, a new view is created in the default database. To create the view explicitly in a given database, use db_name.view_name syntax to qualify the view name with the database name: CREATE VIEW test.v AS SELECT * FROM t;Unqualified table or view names in the SELECT statement are also interpreted with respect to the default database. A view can refer to tables or views in other databases by qualifying the table or view name with the appropriate database name. Within a database, base tables and views share the same namespace, so a base table and a view cannot have the same name. Columns retrieved by the SELECT statement can be simple references to table columns, or expressions that use functions, constant values, operators, and so forth. A view must have unique column names with no duplicates, just like a base table. By default, the names of the columns retrieved by the SELECT statement are used for the view column names. To define explicit names for the view columns, specify the optional column_list clause as a list of comma-separated identifiers. The number of names in column_list must be the same as the number of columns retrieved by the SELECT statement. A view can be created from many kinds of SELECT statements. It can refer to base tables or other views. It can use joins, UNION, and subqueries. The SELECT need not even refer to any tables: CREATE VIEW v_today (today) AS SELECT CURRENT_DATE;The following example defines a view that selects two columns from another table as well as an expression calculated from those columns: mysql> CREATE TABLE t (qty INT, price INT); mysql> INSERT INTO t VALUES(3, 50); mysql> CREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t; mysql> SELECT * FROM v; +------+-------+-------+ | qty | price | value | +------+-------+-------+ | 3 | 50 | 150 | +------+-------+-------+A view definition is subject to the following restrictions:
ORDER BY is permitted in a view definition, but it is ignored if you select from a view using a statement that has its own ORDER BY. For other options or clauses in the definition, they are added to the options or clauses of the statement that references the view, but the effect is undefined. For example, if a view definition includes a LIMIT clause, and you select from the view using a statement that has its own LIMIT clause, it is undefined which limit applies. This same principle applies to options such as ALL, DISTINCT, or SQL_SMALL_RESULT that follow the SELECT keyword, and to clauses such as INTO, FOR UPDATE, FOR SHARE, LOCK IN SHARE MODE, and PROCEDURE. The results obtained from a view may be affected if you change the query processing environment by changing system variables: mysql> CREATE VIEW v (mycol) AS SELECT 'abc'; Query OK, 0 rows affected (0.01 sec) mysql> SET sql_mode = ''; Query OK, 0 rows affected (0.00 sec) mysql> SELECT "mycol" FROM v; +-------+ | mycol | +-------+ | mycol | +-------+ 1 row in set (0.01 sec) mysql> SET sql_mode = 'ANSI_QUOTES'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT "mycol" FROM v; +-------+ | mycol | +-------+ | abc | +-------+ 1 row in set (0.00 sec)The DEFINER and SQL SECURITY clauses determine which MySQL account to use when checking access privileges for the view when a statement is executed that references the view. The valid SQL SECURITY characteristic values are DEFINER (the default) and INVOKER. These indicate that the required privileges must be held by the user who defined or invoked the view, respectively. If the DEFINER clause is present, the user value should be a MySQL account specified as 'user_name'@'host_name', CURRENT_USER, or CURRENT_USER(). The permitted user values depend on the privileges you hold, as discussed in Section 25.6, “Stored Object Access Control”. Also see that section for additional information about view security. If the DEFINER clause is omitted, the default definer is the user who executes the CREATE VIEW statement. This is the same as specifying DEFINER = CURRENT_USER explicitly. Within a view definition, the CURRENT_USER function returns the view's DEFINER value by default. For views defined with the SQL SECURITY INVOKER characteristic, CURRENT_USER returns the account for the view's invoker. For information about user auditing within views, see Section 6.2.23, “SQL-Based Account Activity Auditing”. Within a stored routine that is defined with the SQL SECURITY DEFINER characteristic, CURRENT_USER returns the routine's DEFINER value. This also affects a view defined within such a routine, if the view definition contains a DEFINER value of CURRENT_USER. MySQL checks view privileges like this:
Example: A view might depend on a stored function, and that function might invoke other stored routines. For example, the following view invokes a stored function f(): CREATE VIEW v AS SELECT * FROM t WHERE t.id = f(t.name);Suppose that f() contains a statement such as this: IF name IS NULL then CALL p1(); ELSE CALL p2(); END IF;The privileges required for executing statements within f() need to be checked when f() executes. This might mean that privileges are needed for p1() or p2(), depending on the execution path within f(). Those privileges must be checked at runtime, and the user who must possess the privileges is determined by the SQL SECURITY values of the view v and the function f(). The DEFINER and SQL SECURITY clauses for views are extensions to standard SQL. In standard SQL, views are handled using the rules for SQL SECURITY DEFINER. The standard says that the definer of the view, which is the same as the owner of the view's schema, gets applicable privileges on the view (for example, SELECT) and may grant them. MySQL has no concept of a schema “owner”, so MySQL adds a clause to identify the definer. The DEFINER clause is an extension where the intent is to have what the standard has; that is, a permanent record of who defined the view. This is why the default DEFINER value is the account of the view creator. The optional ALGORITHM clause is a MySQL extension to standard SQL. It affects how MySQL processes the view. ALGORITHM takes three values: MERGE, TEMPTABLE, or UNDEFINED. For more information, see Section 25.5.2, “View Processing Algorithms”, as well as Section 8.2.2.4, “Optimizing Derived Tables, View References, and Common Table Expressions with Merging or Materialization”. Some views are updatable. That is, you can use them in statements such as UPDATE, DELETE, or INSERT to update the contents of the underlying table. For a view to be updatable, there must be a one-to-one relationship between the rows in the view and the rows in the underlying table. There are also certain other constructs that make a view nonupdatable. A generated column in a view is considered updatable because it is possible to assign to it. However, if such a column is updated explicitly, the only permitted value is DEFAULT. For information about generated columns, see Section 13.1.20.8, “CREATE TABLE and Generated Columns”. The WITH CHECK OPTION clause can be given for an updatable view to prevent inserts or updates to rows except those for which the WHERE clause in the select_statement is true. In a WITH CHECK OPTION clause for an updatable view, the LOCAL and CASCADED keywords determine the scope of check testing when the view is defined in terms of another view. The LOCAL keyword restricts the CHECK OPTION only to the view being defined. CASCADED causes the checks for underlying views to be evaluated as well. When neither keyword is given, the default is CASCADED. For more information about updatable views and the WITH CHECK OPTION clause, see Section 25.5.3, “Updatable and Insertable Views”, and Section 25.5.4, “The View WITH CHECK OPTION Clause”. Page 12
13.1.24 DROP DATABASE StatementDROP {DATABASE | SCHEMA} [IF EXISTS] db_nameDROP DATABASE drops all tables in the database and deletes the database. Be very careful with this statement! To use DROP DATABASE, you need the DROP privilege on the database. DROP SCHEMA is a synonym for DROP DATABASE.
Important When a database is dropped, privileges granted specifically for the database are not automatically dropped. They must be dropped manually. See Section 13.7.1.6, “GRANT Statement”. IF EXISTS is used to prevent an error from occurring if the database does not exist. If the default database is dropped, the default database is unset (the DATABASE() function returns NULL). If you use DROP DATABASE on a symbolically linked database, both the link and the original database are deleted. DROP DATABASE returns the number of tables that were removed. The DROP DATABASE statement removes from the given database directory those files and directories that MySQL itself may create during normal operation. This includes all files with the extensions shown in the following list:
If other files or directories remain in the database directory after MySQL removes those just listed, the database directory cannot be removed. In this case, you must remove any remaining files or directories manually and issue the DROP DATABASE statement again. Dropping a database does not remove any TEMPORARY tables that were created in that database. TEMPORARY tables are automatically removed when the session that created them ends. See Section 13.1.20.2, “CREATE TEMPORARY TABLE Statement”. You can also drop databases with mysqladmin. See Section 4.5.2, “mysqladmin — A MySQL Server Administration Program”. Page 13
13.1.25 DROP EVENT StatementDROP EVENT [IF EXISTS] event_nameThis statement drops the event named event_name. The event immediately ceases being active, and is deleted completely from the server. If the event does not exist, the error ERROR 1517 (HY000): Unknown event 'event_name' results. You can override this and cause the statement to generate a warning for nonexistent events instead using IF EXISTS. This statement requires the EVENT privilege for the schema to which the event to be dropped belongs. |