Insert¶
This page explains how to construct SQL INSERT statements using ts-sql-query. It covers single and multiple inserts, inserts from SELECT, column shape mapping, value manipulation methods, and how to handle conflicts using onConflictDoNothing or onConflictDoUpdateSet for upsert behavior.
Insert one row¶
const insertCustomer = connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
}).setIfNotSet({
birthday: new Date()
}).returningLastInsertedId()
.executeInsert();
The executed query is:
The parameters are: [ 'John', 'Smith', 1, 2019-08-16T15:02:32.849Z ] (On Oracle, output parameters are added at the corresponding position with the structure {dir:3003})
The result type is a promise with the id of the last inserted row:
Insert multiple values¶
const valuesToInsert = [
{
firstName: 'John',
lastName: 'Smith',
companyId: 1
},
{
firstName: 'Other',
lastName: 'Person',
companyId: 1
}
]
const insertMultipleCustomers = connection.insertInto(tCustomer)
.values(valuesToInsert)
.returningLastInsertedId()
.executeInsert();
The executed query is:
The parameters are: [ 'John', 'Smith', 1, 'Other', 'Person', 1 ] (On Oracle, output parameters are added at the corresponding position with the structure {dir:3003})
The result type is a promise with the id of the last inserted rows:
Note
Return the last inserted id of an insert with multiple rows is only supported by PostgreSQL, SQL Server and Oracle. If you try to use it with other database you will get a compilation error.
Insert from select¶
const insertCustomersFromSelect = connection.insertInto(tCustomer)
.from(
connection.selectFrom(tCustomer)
.where(
tCustomer.companyId.equals(1)
)
.select({
firstName: tCustomer.firstName,
lastName: tCustomer.lastName,
companyId: tCustomer.companyId
})
)
.executeInsert();
The executed query is:
The parameters are: [ 1 ]
The result type is a promise with the number of inserted rows:
Insert returning¶
If you are using PostgreSQL, modern SQLite, SQL Server, modern MariaDB or Oracle (except for an insert from select), you can return values of the inserted record in the same query using the returning or returningOneColumn methods.
const insertReturningCustomerData = connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
}).returning({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.executeInsertOne()
The executed query is:
The parameters are: [ 'John', 'Smith', 1 ] (On Oracle, output parameters are added at the corresponding position with the structure {dir:3003})
The result type is a promise with the information of the inserted rows:
Other options
You can project optional values in objects as always-required properties that allow null calling projectingOptionalValuesAsNullable() immediately after returning(...).
You can execute the query using:
executeInsertNoneOrOne(): Promise<RESULT | null>: Execute the insert query that returns one or no result from the database. In case of more than one result found an exception will be thrown.executeInsertOne(): Promise<RESULT>: Execute the insert query that returns one result from the database. If no result is returned by the database an exception will be thrown.executeInsertMany(min?: number, max?: number): Promise<RESULT[]>: Execute the insert query that returns zero or many results from the database.
Aditionally, if you want to return the value of a single column, you can use returningOneColumn(column) instead of returning({...}).
Insert with value's shape¶
You can specify the object's shape that contains the values to insert. This shape allows you to map each property in the values to insert with the columns in the table; in that way, the property in the value doesn't need to have the same name. The only values to be inserted are the ones included in the shape. Additionally, you can extend the shape later to allow set additional properties in future set over this query. Be aware the shape can be a subset of the required columns; in that case, you will get a compilation error (you will not be able to call the execute methods) if you don't extend the shape by adding the missing keys and setting the proper values.
const customerToInsert = {
customerFirstName: 'John',
customerLastName: 'Smith'
}
const currentCompanyId = 23
const insertCustomer = connection.insertInto(tCustomer)
.shapedAs({
customerFirstName: 'firstName',
customerLastName: 'lastName'
}).set(customerToInsert)
.extendShape({
customerCompanyId: 'companyId'
}).set({
customerCompanyId: currentCompanyId
}).returningLastInsertedId()
.executeInsert()
The executed query is:
The parameters are: [ "John", "Smith", 23 ] (On Oracle, output parameters are added at the corresponding position with the structure {dir:3003})
The result type is a promise with the id of the last inserted row:
Insert multiple with value' shape¶
You can specify the object's shape that contains the values to insert. This shape allows you to map each property in the values to insert with the columns in the table; in that way, the property in the value doesn't need to have the same name. The only values to be inserted are the ones included in the shape. Additionally, you can extend the shape later to allow set additional properties in future set over this query. Be aware the shape can be a subset of the required columns. If any required column is missing, you will get a compilation error and the query execution methods (such as executeInsert()) will not be available if you don't extend the shape by adding the missing keys and setting the proper values.
const customersToInsert = [
{
customerFirstName: 'John',
customerLastName: 'Smith'
},
{
customerFirstName: 'Other',
customerLastName: 'Person'
}
]
const currentCompanyId = 23
const insertMultipleCustomers = await connection.insertInto(tCustomer)
.shapedAs({
customerFirstName: 'firstName',
customerLastName: 'lastName'
})
.values(customersToInsert)
.extendShape({
customerCompanyId: 'companyId'
}).setForAll({
customerCompanyId: currentCompanyId
}).returningLastInsertedId()
.executeInsert()
The executed query is:
The parameters are: [ "John", "Smith", 23, "Other", "Person", 23 ] (On Oracle, output parameters are added at the corresponding position with the structure {dir:3003})
The result type is a promise with the id of the last inserted rows:
Manipulating values to insert¶
ts-sql-query offers many commodity methods to manipulate the data to insert, allowing adding missing values, deleting undesired values, or throwing an error if a value is present.
When you write your insert query, you set the initial value calling:
interface InsertExpression {
/** Alias to set method: Set the values for insert */
values(columns: InsertSets): this
set(columns: InsertSets): this
dynamicSet(): this
dynamicSet(columns: OptionalInsertSets): this
/** Alias to dynamicSet method: Allows to set the values dynamically */
dynamicValues(columns: OptionalInsertSets): this
}
The set and values methods will require you to provide a value at least for the required fields. The dynamicSet and dynamicValues methods allow you to start the insert with optional values even when required in the insert; or even with no values at all. ts-sql-query will track all missing properties, and you will get a compilation error if one of them is missed (you will not be able to call the execute methods).
When you set the initial value, you can start manipulating them using the following methods:
interface InsertExpression {
/**
* Set the values for insert.
*/
set(columns: InsertSets): this
/**
* Set a value only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array.
*/
setIfValue(columns: OptionalInsertSets): this
/**
* Set a previous set value only.
*/
setIfSet(columns: InsertSets): this
/**
* Set a previous set value only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array.
*/
setIfSetIfValue(columns: OptionalInsertSets): this
/**
* Set a value only if it was not previously set.
*/
setIfNotSet(columns: InsertSets): this
/**
* Set a unset value only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array
* (only if the value was not previously set).
*/
setIfNotSetIfValue(columns: OptionalInsertSets): this
/**
* Set a value for the specified columns that was previously indicated a value for set.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
setIfHasValue(columns: InsertSets): this
/**
* Set a value for the specified columns that was previously indicated a value for
* set only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
setIfHasValueIfValue(columns: OptionalInsertSets): this
/**
* Set a value for the specified columns that has not value to set.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
setIfHasNoValue(columns: InsertSets): this
/**
* Set a value for the specified columns that has no value to set
* only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
setIfHasNoValueIfValue(columns: OptionalInsertSets): this
/**
* Unset the listed columns previous set.
* */
ignoreIfSet(...columns: string[]): this
/**
* Keep only the listed columns previous set.
*/
keepOnly(...columns: string[]): this
/**
* Unset the listed columns if they have a value to set.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
ignoreIfHasValue(...columns: string[]): this
/**
* Unset the listed columns if them has no value to set.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
ignoreIfHasNoValue(...columns: string[]): this
/**
* Unset all columns that was set with no value.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
ignoreAnySetWithNoValue(): this
/**
* Throw an error if the indicated properties are set
*/
disallowIfSet(errorMessage: string, ...columns: string[]): this
disallowIfSet(error: Error, ...columns: string[]): this
/**
* Throw an error if the indicated properties are not set
*/
disallowIfNotSet(errorMessage: string, ...columns: string[]): this
disallowIfNotSet(error: Error, ...columns: string[]): this
/**
* Throw an error if the indicated properties was set with a value.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array
*/
disallowIfValue(errorMessage: string, ...columns: string[]): this
disallowIfValue(error: Error, ...columns: string[]): this
/**
* Throw an error if the indicated properties was set not set or has no value.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array
*/
disallowIfNoValue(errorMessage: string, ...columns: string[]): this
disallowIfNoValue(error: Error, ...columns: string[]): this
/**
* Throw an error if any column other than the ones listed is set
*/
disallowAnyOtherSet(errorMessage: string, ...columns: string[]): this
disallowAnyOtherSet(error: Error, ...columns: string[]): this
}
All these methods have a When variant that allows you to specify as the first argument a boolean that, when it is true, the action will be executed. Like: setWhen(when: boolean, columns: InsertSets): this
Manipulating values to insert (multiple)¶
ts-sql-query offers many commodity methods to manipulate the data to insert, allowing adding missing values, deleting undesired values, or throwing an error if a value is present.
When you write your insert query, you set the initial value calling:
interface InsertExpression {
values(columns: InsertSets[]): this
dynamicValues(columns: OptionalInsertSets[]): this
}
The values method will require you to provide a value at least for the required fields. The dynamicValues method allows you to start the insert with optional values even when required in the insert; or even with no values at all. ts-sql-query will track all missing properties, and you will get a compilation error if one of them is missed (you will not be able to call the execute methods).
When you set the initial value, you can start manipulating them (all at the same time, with same new values) using the following methods:
interface InsertExpression {
/**
* Set the values for insert
*/
setForAll(columns: InsertSets): this
/**
* Set a value only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array
*/
setForAllIfValue(columns: OptionalInsertSets): this
/**
* Set a previous set value only
*/
setForAllIfSet(columns: InsertSets): this
/**
* Set a previous set value only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array
*/
setForAllIfSetIfValue(columns: OptionalInsertSets): this
/**
* Set a unset value (only if the value was not previously set)
*/
setForAllIfNotSet(columns: InsertSets): this
/**
* Set a unset value only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array
* (only if the value was not previously set)
*/
setForAllIfNotSetIfValue(columns: OptionalInsertSets): this
/**
* Set a value for the specified columns that was previously indicated a value for set.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array
*/
setForAllIfHasValue(columns: InsertSets): this
/**
* Set a value for the specified columns that was previously indicated a value for
* set only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array
*/
setForAllIfHasValueIfValue(columns: OptionalInsertSets): this
/**
* Set a value for the specified columns that has not value to set.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array
*/
setForAllIfHasNoValue(columns: InsertSets): this
/**
* Set a value for the specified columns that has no value to set
* only if the provided value is not null, undefined, empty string
* (only when the allowEmptyString flag in the connection is not set to true,
* that is the default behaviour) or an empty array.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array
*/
setForAllIfHasNoValueIfValue(columns: OptionalInsertSets): this
/**
* Unset the listed columns previous set.
* */
ignoreIfSet(...columns: string[]): this
/**
* Keep only the listed columns previous set.
*/
keepOnly(...columns: string[]): this
/**
* Unset the listed columns if they have a value to set.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
ignoreIfHasValue(...columns: string[]): this
/**
* Unset the listed columns if them has no value to set.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
ignoreIfHasNoValue(...columns: string[]): this
/**
* Unset all columns that was set with no value.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array.
*/
ignoreAnySetWithNoValue(): this
/**
* Throw an error if the indicated properties are set
*/
disallowIfSet(errorMessage: string, ...columns: string[]): this
disallowIfSet(error: Error, ...columns: string[]): this
/**
* Throw an error if the indicated properties are not set
*/
disallowIfNotSet(errorMessage: string, ...columns: string[]): this
disallowIfNotSet(error: Error, ...columns: string[]): this
/**
* Throw an error if the indicated properties was set with a value.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array
*/
disallowIfValue(errorMessage: string, ...columns: string[]): this
disallowIfValue(error: Error, ...columns: string[]): this
/**
* Throw an error if the indicated properties was set not set or has no value.
* It is considered the column has value if it was set with a value that is not null,
* undefined, empty string (only when the allowEmptyString flag in the connection is not
* set to true, that is the default behaviour) or an empty array
*/
disallowIfNoValue(errorMessage: string, ...columns: string[]): this
disallowIfNoValue(error: Error, ...columns: string[]): this
/**
* Throw an error if any column other than the ones listed is set
*/
disallowAnyOtherSet(errorMessage: string, ...columns: string[]): this
disallowAnyOtherSet(error: Error, ...columns: string[]): this
}
All these methods have a When variant that allows you to specify as the first argument a boolean that, when it is true, the action will be executed. Like: setForAll(columns: InsertSets): this
Insert on conflict do nothing¶
If you are using PostgreSQL, SQLite, MariaDB or MySQL you can specify the insert must do nothing in case of conflict.
const insertReturningCustomerData = connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
})
.onConflictDoNothing()
.returning({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.executeInsertNoneOrOne()
The executed query is:
The parameters are: [ 'John', 'Smith', 1 ]
The result type is a promise with the information of the inserted rows:
const insertReturningCustomerData: Promise<{
id: number;
firstName: string;
lastName: string;
} | null>
Note
- On PostgreSQL and SQLite, you can specify the columns that can create the conflict (including a
whereclause for that columns). - On PostgreSQL you can specify the constraint name that raise the conflict.
- You can combine this with other insert's features, e.g. return some columns.
Insert on conflict do update ("upsert")¶
PostgreSQL, SQLite, MariaDB and MySQL support specifying that an INSERT must do an update in case of conflict (also known as an "upsert"). The shape of the call depends on the engine:
- On MariaDB and MySQL the update fires on any unique-key violation; you don't specify which one. Use the bare
.onConflictDoUpdateSet({...}). - On PostgreSQL you must say which unique-key column or constraint triggers the update. Use
.onConflictOn(...)(or.onConflictOnConstraint(connection.rawFragment\...`)) chained with.doUpdateSet({...})`. - SQLite accepts both forms.
- Oracle and SQL Server don't support this syntax.
const insertReturningCustomerData = await connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
})
.onConflictDoUpdateSet({ // any unique-key violation triggers the update
companyId: 1
})
.returning({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.executeInsertOne()
const insertReturningCustomerData = await connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
})
.onConflictOn(tCustomer.id) // PostgreSQL requires an explicit conflict target
.doUpdateSet({
companyId: 1
})
.returning({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.executeInsertOne()
const insertReturningCustomerData = await connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
})
.onConflictOn(tCustomer.id) // both forms accepted; the targeted form ports to PostgreSQL
.doUpdateSet({
companyId: 1
})
.returning({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.executeInsertOne()
The executed query is:
The parameters are: [ 'John', 'Smith', 1, 1 ]
The result type is a promise with the information of the inserted rows:
If you want to use in the update the values to insert you can call them method valuesForInsert() over the table to get access to the table representation of the values to insert.
const tCustomerForInsert = tCustomer.valuesForInsert()
const insertReturningCustomerData = await connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
})
.onConflictDoUpdateSet({ // any unique-key violation triggers the update
firstName: tCustomer.firstName.concat(' - ').concat(tCustomerForInsert.firstName),
lastName: tCustomer.lastName.concat(' - ').concat(tCustomerForInsert.lastName)
})
.returning({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.executeInsertOne()
const tCustomerForInsert = tCustomer.valuesForInsert()
const insertReturningCustomerData = await connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
})
.onConflictOn(tCustomer.id) // PostgreSQL requires an explicit conflict target
.doUpdateSet({
firstName: tCustomer.firstName.concat(' - ').concat(tCustomerForInsert.firstName),
lastName: tCustomer.lastName.concat(' - ').concat(tCustomerForInsert.lastName)
})
.returning({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.executeInsertOne()
const tCustomerForInsert = tCustomer.valuesForInsert()
const insertReturningCustomerData = await connection.insertInto(tCustomer).set({
firstName: 'John',
lastName: 'Smith',
companyId: 1
})
.onConflictOn(tCustomer.id) // both forms accepted; the targeted form ports to PostgreSQL
.doUpdateSet({
firstName: tCustomer.firstName.concat(' - ').concat(tCustomerForInsert.firstName),
lastName: tCustomer.lastName.concat(' - ').concat(tCustomerForInsert.lastName)
})
.returning({
id: tCustomer.id,
firstName: tCustomer.firstName,
lastName: tCustomer.lastName
})
.executeInsertOne()
The executed query is:
insert into customer (first_name, last_name, company_id)
values ($1, $2, $3)
on conflict (id) do update set
first_name = customer.first_name || $4 || excluded.first_name,
last_name = customer.last_name || $5 || excluded.last_name
returning
id as id,
first_name as "firstName",
last_name as "lastName"
The parameters are: [ 'John', 'Smith', 1, ' - ', ' - ' ]
The result type is a promise with the information of the inserted rows:
Note
- On PostgreSQL and SQLite, the targeted form additionally accepts a
whereclause that restricts when the update must be performed. - On PostgreSQL you can also identify the conflict by constraint name with
.onConflictOnConstraint(connection.rawFragment\my_constraint`)`. The constraint name is a SQL identifier, not a runtime value, so it must be supplied as a raw SQL fragment — the caller is expected to assemble it from database introspection rather than from a user-influenced string. - You can combine any of these forms with other insert features, e.g. return some columns.