creating a new table (relation)
Available in
DSQL, ESQL
Syntax
The CREATE TABLE
statement creates a new table. Any user can create it and its name must be unique among the names of all tables, views and stored procedures in the database.
A table must contain at least one column that is not computed and the names of columns must be unique in the table.
A column must have either an explicit SQL data type, the name of a domain whose attributes will be copied for the column, or be defined as COMPUTED BY
an expression (a calculated field).
A table may have any number of table constraints, including none.
In Firebird, columns are nullable by default. The optional NOT NULL
clause specifies that the column cannot take NULL
in place of a value.
Character Columns
You can use the CHARACTER SET
clause to specify the character set for the CHAR
, VARCHAR
and BLOB
(text subtype) types. If the character set is not specified, the character set specified during the creation of the database will be used by default. If no character set was specified during the creation of the database, the NONE
character set is applied by default. In this case, data is stored and retrieved the way it was submitted. Data in any encoding can be added to such a column, but it is not possible to add this data to a column with a different encoding. No transliteration is performed between the source and destination encodings, which may result in errors.
The optional COLLATE
clause allows you to specify the collation sequence for character data types, including BLOB SUB_TYPE TEXT
. If no collation sequence is specified, the collation sequence that is default for the specified character set during the creation of the column is applied by default.
Setting a DEFAULT
Value
The optional DEFAULT
clause allows you to specify the default value for the table column. This value will be added to the column when an INSERT
statement is executed if no value was specified for it and that column was omitted from the INSERT
command.
The default value can be a literal of a compatible type, a context variable that is type-compatible with the data type of the column, or NULL
, if the column allows it. If no default value is explicitly specified, NULL
is implied.
An expression cannot be used as a default value.
To define a column, you can use a previously defined domain. If the definition of a column is based on a domain, it may contain a new default value, additional CHECK
constraints and a COLLATE
clause that will override the values specified in the domain definition. The definition of such a column may contain additional column constraints (for instance, NOT NULL
), if the domain does not have it.
It is not possible to define a domain-based column that is nullable if the domain was defined with the |
Calculated Fields
Calculated fields can be defined with the COMPUTED [BY]
or GENERATED ALWAYS AS
clause (according to the SQL:2003 standard). They mean the same. Describing the data type is not required (but possible) for calculated fields, as the DBMS calculates and stores the appropriate type as a result of the expression analysis. Appropriate operations for the data types included in an expression must be specified precisely.
If the data type is explicitly specified for a calculated field, the calculation result is converted to the specified type. This means, for instance, that the result of a numeric expression could be rendered as a string.
In a query that selects a COMPUTED BY
column, the expression is evaluated for each row of the selected data.
Defining an ARRAY
Column
If the column is to be an array, the base type can be any SQL data type except
BLOB
andARRAY
.The dimensions of the array are specified between square brackets. (In the these brackets appear in quotes to distinguish them from the square brackets that identify optional syntax elements.)
For each array dimension, one or two integer numbers define the lower and upper boundaries of its index range:
By default, arrays are 1-based. The lower boundary is implicit and only the upper boundary need be specified. A single number smaller than 1 defines the range num..1 and a number greater than 1 defines the range 1..num.
Two numbers separated by a colon (‘
:
’) and optional whitespace, the second greater than the first, can be used to define the range explicitly. One or both boundaries can be less than zero, as long as the upper boundary is greater than the lower.
When the array has multiple dimensions, the range definitions for each dimension must be separated by commas and optional whitespace.
Subscripts are validated only if an array actually exists. It means that no error messages regarding invalid subscripts will be returned if selecting a specific element returns nothing or if an array field is
NULL
.
Four types of constraints can be specified. They are:
Primary key (
PRIMARY KEY
)Unique key (
UNIQUE
)Foreign key (
REFERENCES
)CHECK
constraint (CHECK
)
Constraints can be specified at column level (“column constraints”) or at table level (“table constraints”). Table-level constraints are needed when keys (uniqueness constraint, Primary Key, Foreign Key) are to be formed across multiple columns and when a CHECK
constraint involves other columns in the row besides the column being defined. Syntax for some types of constraint may differ slightly according to whether the constraint is being defined at column or table level.
A column-level constraint is specified during a column definition, after all column attributes except
COLLATION
are specified, and can involve only the column specified in that definitionTable-level constraints are specified after all of the column definitions. They are a more flexible way to set constraints, since they can cater for constraints involving multiple columns
You can mix column-level and table-level constraints in the same
CREATE TABLE
statement
The system automatically creates the corresponding index for a primary key (PRIMARY KEY
), a unique key (UNIQUE
) and a foreign key (REFERENCES
for a column-level constraint, FOREIGN KEY REFERENCES
for one at the table level).
Names for Constraints and Their Indexes
Column-level constraints and their indexes are named automatically:
The constraint name has the form
INTEG_n
, where n represents one or more digitsThe index name has the form
RDB$PRIMARYn
(for a primary key index),RDB$FOREIGNn
(for a foreign key index) orRDB$n
(for a unique key index). Again, n represents one or more digits.
Automatic naming of table-level constraints and their indexes follows the same pattern, unless the names are supplied explicitly.
Named Constraints
A constraint can be named explicitly if the CONSTRAINT
clause is used for its definition. While the CONSTRAINT
clause is optional for defining column-level constraints, it is mandatory for table-level. By default, the constraint index will have the same name as the constraint. If a different name is wanted for the constraint index, a USING
clause can be included.
The USING
Clause
The USING
clause allows you to specify a user-defined name for the index that is created automatically and, optionally, to define the direction of the index — either ascending (the default) or descending.
PRIMARY KEY
The PRIMARY KEY
constraint is built on one or more key columns, each column having the NOT NULL
constraint specified for it. The values across the key columns in any row must be unique. A table can have only one primary key.
A single-column Primary Key can be defined as a column level or a table-level constraint
A multi-column Primary Key must be specified as a table-level constraint
The UNIQUE
Constraint
The UNIQUE
constraint defines the requirement of content uniqueness for the values in a key throughout the table. A table can contain any number of unique key constraints.
As with the Primary Key, the Unique constraint can be multi-column. If so, it must be specified as a table-level constraint.
NULL
in Unique Keys
Firebird’s SQL-99-compliant rules for UNIQUE
constraints allow one or more NULL
s in a column with a UNIQUE
constraint. That makes it possible to define a UNIQUE
constraint on a column that does not have the NOT NULL
constraint.
For UNIQUE
keys that span multiple columns, the logic is a little complicated:
Multiple rows having null in all the columns of the key are allowed
Multiple rows having keys with different combinations of nulls and non-null values are allowed
Multiple rows having the same key columns null and the rest filled with non-null values are allowed, provided the values differ in at least one column
Multiple rows having the same key columns null and the rest filled with non-null values that are the same in every column will violate the constraint
The rules for uniqueness can be summarised thus:
Illustration
RECREATE TABLE t( x int, y int, z int, unique(x,y,z));
INSERT INTO t values( NULL, 1, 1 );
INSERT INTO t values( NULL, NULL, 1 );
INSERT INTO t values( NULL, NULL, NULL );
INSERT INTO t values( NULL, NULL, NULL ); -- Permitted
FOREIGN KEY
A Foreign Key ensures that the participating column(s) can contain only values that also exist in the referenced column(s) in the master table. These referenced columns are often called target columns. They must be the primary key or a unique key in the target table. They need not have a NOT NULL
constraint defined on them although, if they are the primary key, they will, of course, have that constraint.
A single-column Foreign Key can be defined in the column declaration, using the keyword REFERENCES
:
... ,
ARTIFACT_ID INTEGER REFERENCES COLLECTION (ARTIFACT_ID),
The column ARTIFACT_ID
in the example references a column of the same name in the table COLLECTIONS
.
Both single-column and multi-column foreign keys can be defined at the table level. For a multi-column Foreign Key, the table-level declaration is the only option. This method also enables the provision of an optional name for the constraint:
...
CONSTRAINT FK_ARTSOURCE FOREIGN KEY(DEALER_ID, COUNTRY)
REFERENCES DEALER (DEALER_ID, COUNTRY),
Notice that the column names in the referenced (“master”) table may differ from those in the Foreign Key.
If no target columns are specified, the Foreign Key automatically references the target table’s Primary Key. |
Foreign Key Actions
With the sub-clauses ON UPDATE
and ON DELETE
it is possible to specify an action to be taken on the affected foreign key column(s) when referenced values in the master table are changed:
NO ACTION
(the default) - Nothing is done
CASCADE
The change in the master table is propagated to the corresponding row(s) in the child table. If a key value changes, the corresponding key in the child records changes to the new value; if the master row is deleted, the child records are deleted.
SET DEFAULT
The Foreign Key columns in the affected rows will be set to their default values as they were when the foreign key constraint was defined.
SET NULL
The Foreign Key columns in the affected rows will be set to NULL
.
The specified action, or the default NO ACTION
, could cause a Foreign Key column to become invalid. For example, it could get a value that is not present in the master table, or it could become NULL
while the column has a NOT NULL
constraint. Such conditions will cause the operation on the master table to fail with an error message.
Example
...
CONSTRAINT FK_ORDERS_CUST
FOREIGN KEY (CUSTOMER) REFERENCES CUSTOMERS (ID)
CHECK
Constraint
The CHECK
constraint defines the condition the values inserted in this column must satisfy. A condition is a logical expression (also called a predicate) that can return the TRUE, FALSE and UNKNOWN values. A condition is considered satisfied if the predicate returns TRUE or value UNKNOWN (equivalent to NULL
). If the predicate returns FALSE, the value will not be accepted. This condition is used for inserting a new row into the table (the INSERT
statement) and for updating the existing value of the table column (the UPDATE
statement) and also for statements where one of these actions may take place (UPDATE OR INSERT, MERGE).
CHECK
conditions — whether defined at table level or column level — refer to table columns by their names. The use of the keyword VALUE
as a placeholder, as in domain CHECK
constraints, is not valid in the context of defining column constraints.
Example
with two column-level constraints and one at table-level:
CREATE TABLE PLACES (
...
LAT DECIMAL(9, 6) CHECK (ABS(LAT) <= 90),
LON DECIMAL(9, 6) CHECK (ABS(LON) <= 180),
...
CONSTRAINT CHK_POLES CHECK (ABS(LAT) < 90 OR LON = 0)
);
Global Temporary Tables (GTT)
Global temporary tables have persistent metadata, but their contents are transaction-bound (the default) or connection-bound. Every transaction or connection has its own private instance of a GTT, isolated from all the others. Instances are only created if and when the GTT is referenced. They are destroyed when the transaction ends or on disconnection. The metadata of a GTT can be modified or removed using ALTER TABLE and DROP TABLE, respectively.
Syntax
Syntax notes
|
Restrictions on GTTs
GTTs can be “dressed up” with all the features and paraphernalia of ordinary tables (keys, references, indexes, triggers and so on) but there are a few restrictions:
GTTs and regular tables cannot reference one another
A connection-bound (“
PRESERVE ROWS
”) GTT cannot reference a transaction-bound (“DELETE ROWS
”) GTTDomain constraints cannot reference any GTT
The destruction of a GTT instance at the end of its life cycle does not cause any
BEFORE
/AFTER
delete triggers to fire
External Tables
The optional EXTERNAL [FILE]
clause specifies that the table is stored outside the database in an external text file of fixed-length records. The columns of a table stored in an external file can be of any type except BLOB
or ARRAY
, although for most purposes, only columns of CHAR
types would be useful.
All you can do with a table stored in an external file is insert new rows (INSERT
) and query the data (SELECT
). Updating existing data (UPDATE
) and deleting rows (DELETE
) are not possible.
A file that is defined as an external table must be located on a storage device that is physically present on the machine where the Firebird server runs and, if the parameter ExternalFileAccess in the firebird.conf
configuration file is Restrict
, it must be in one of the directories listed there as the argument for Restrict
. If the file does not exist yet, Firebird will create it on first access.
The ability to use external files for a table depends on the value set for the ExternalFileAccess parameter in
|
External File Format
The “row” format of the external table is fixed length. There are no field delimiters: both field and row boundaries are determined by maximum sizes, in bytes, of the field definitions. It is important to keep this in mind, both when defining the structure of the external table and when designing an input file for an external table that is to import data from another application. The ubiquitous “.csv” format, for example, is of no use as an input file and cannot be generated directly into an external file.
The most useful data type for the columns of external tables is the fixed-length CHAR
type, of suitable lengths for the data they are to carry. Date and number types are easily cast to and from strings whereas, unless the files are to be read by another Firebird database, the native data types will appear to external applications as unparseable “alphabetti”.
Of course, there are ways to manipulate typed data so as to generate output files from Firebird that can be read directly as input files to other applications, using stored procedures, with or without employing external tables. Such techniques are beyond the scope of a language reference. Here, we provide some guidelines and tips for producing and working with simple text files, since the external table feature is often used as an easy way to produce or read transaction-independent logs that can be studied off-line in a text editor or auditing application.
Row Delimiters
Generally, external files are more useful if rows are separated by a delimiter, in the form of a “newline” sequence that is recognised by reader applications on the intended platform. For most contexts on Windows, it is the two-byte ‘CRLF’ sequence, carriage return (ASCII code decimal 13) and line feed (ASCII code decimal 10). On POSIX, LF on its own is usual; for some MacOSX applications, it may be LFCR. There are various ways to populate this delimiter column. In our example below, it is done by using a trigger and the internal function ASCII_CHAR
.
External Table Example
For our example, we will define an external log table that might be used by an exception handler in a stored procedure or trigger. The external table is chosen because the messages from any handled exceptions will be retained in the log, even if the transaction that launched the process is eventually rolled back because of another, unhandled exception. For demonstration purposes, it has just two data columns, a time stamp and a message. The third column stores the row delimiter:
CREATE TABLE ext_log
EXTERNAL FILE 'd:\externals\log_me.txt' (
stamp CHAR (24),
message CHAR(100),
crlf CHAR(2) -- for a Windows context
);
COMMIT;
Now, a trigger, to write the timestamp and the row delimiter each time a message is written to the file:
SET TERM ^;
CREATE TRIGGER bi_ext_log FOR ext_log
ACTIVE BEFORE INSERT
AS
BEGIN
IF (new.stamp is NULL) then
new.stamp = CAST (CURRENT_TIMESTAMP as CHAR(24));
new.crlf = ASCII_CHAR(13) || ASCII_CHAR(10);
END ^
COMMIT ^
SET TERM ;^
Inserting some records (which could have been done by an exception handler or a fan of Shakespeare):
insert into ext_log (message)
values('Shall I compare thee to a summer''s day?');
insert into ext_log (message)
values('Thou art more lovely and more temperate');
The output:
Creating the
COUNTRY
table with the primary key specified as a column constraint.CREATE TABLE COUNTRY (
COUNTRY COUNTRYNAME NOT NULL PRIMARY KEY,
);
Creating the
STOCK
table with the named primary key specified at the column level and the named unique key specified at the table level.CREATE TABLE STOCK (
MODEL SMALLINT NOT NULL CONSTRAINT PK_STOCK PRIMARY KEY,
MODELNAME CHAR(10) NOT NULL,
ITEMID INTEGER NOT NULL,
CONSTRAINT MOD_UNIQUE UNIQUE (MODELNAME, ITEMID)
);
Creating the
JOB
table with a primary key constraint spanning two columns, a foreign key constraint for theCOUNTRY
table and a table-levelCHECK
constraint. The table also contains an array of 5 elements.CREATE TABLE JOB (
JOB_CODE JOBCODE NOT NULL,
JOB_GRADE JOBGRADE NOT NULL,
JOB_COUNTRY COUNTRYNAME,
JOB_TITLE VARCHAR(25) NOT NULL,
MIN_SALARY NUMERIC(18, 2) DEFAULT 0 NOT NULL,
MAX_SALARY NUMERIC(18, 2) NOT NULL,
JOB_REQUIREMENT BLOB SUB_TYPE 1,
LANGUAGE_REQ VARCHAR(15) [1:5],
PRIMARY KEY (JOB_CODE, JOB_GRADE),
FOREIGN KEY (JOB_COUNTRY) REFERENCES COUNTRY (COUNTRY)
ON UPDATE CASCADE
ON DELETE SET NULL,
CONSTRAINT CHK_SALARY CHECK (MIN_SALARY < MAX_SALARY)
);
Creating the
PROJECT
table with primary, foreign and unique key constraints with custom index names specified with theUSING
clause.CREATE TABLE PROJECT (
PROJ_ID PROJNO NOT NULL,
PROJ_NAME VARCHAR(20) NOT NULL UNIQUE USING DESC INDEX IDX_PROJNAME,
PROJ_DESC BLOB SUB_TYPE 1,
TEAM_LEADER EMPNO,
PRODUCT PRODTYPE,
CONSTRAINT PK_PROJECT PRIMARY KEY (PROJ_ID) USING INDEX IDX_PROJ_ID,
FOREIGN KEY (TEAM_LEADER) REFERENCES EMPLOYEE (EMP_NO)
USING INDEX IDX_LEADER
);
Creating the
SALARY_HISTORY
table with two computed fields. The first one is declared according to the SQL:2003 standard, while the second one is declared according to the traditional declaration of computed fields in Firebird.CREATE TABLE SALARY_HISTORY (
EMP_NO EMPNO NOT NULL,
CHANGE_DATE TIMESTAMP DEFAULT 'NOW' NOT NULL,
UPDATER_ID VARCHAR(20) NOT NULL,
OLD_SALARY SALARY NOT NULL,
PERCENT_CHANGE DOUBLE PRECISION DEFAULT 0 NOT NULL,
SALARY_CHANGE GENERATED ALWAYS AS
(OLD_SALARY * PERCENT_CHANGE / 100),
NEW_SALARY COMPUTED BY
(OLD_SALARY + OLD_SALARY * PERCENT_CHANGE / 100)
);
Creating a transaction-scoped global temporary table that uses a foreign key to reference a connection-scoped global temporary table. The
ON COMMIT
sub-clause is optional becauseDELETE ROWS
is the default.CREATE GLOBAL TEMPORARY TABLE MYTXGTT (
ID INTEGER NOT NULL PRIMARY KEY,
PARENT_ID INTEGER NOT NULL REFERENCES MYCONNGTT(ID),
TXT VARCHAR(32),
) ON COMMIT DELETE ROWS;