-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add insert with VALUES and functions call example
- Loading branch information
Showing
1 changed file
with
95 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import type { ClickHouseSettings } from '@clickhouse/client' | ||
import { createClient } from '@clickhouse/client' // or '@clickhouse/client-web' | ||
|
||
type Data = { | ||
id: string | ||
timestamp: number | ||
email: string | ||
name: string | null | ||
} | ||
|
||
/** | ||
* An example how to send an INSERT INTO ... VALUES ... query that requires additional functions call. | ||
* Inspired by https://github.com/ClickHouse/clickhouse-js/issues/239 | ||
*/ | ||
void (async () => { | ||
const tableName = 'insert_values_and_functions' | ||
const client = createClient() | ||
// Recommended for cluster usage to avoid situations where a query processing error occurred after the response code | ||
// and HTTP headers were sent to the client, as it might happen before the changes were applied on the server. | ||
// See https://clickhouse.com/docs/en/interfaces/http/#response-buffering | ||
const commandSettings: ClickHouseSettings = { | ||
wait_end_of_query: 1, | ||
} | ||
|
||
// Prepare an example table | ||
await client.command({ | ||
query: `DROP TABLE IF EXISTS ${tableName}`, | ||
clickhouse_settings: commandSettings, | ||
}) | ||
await client.command({ | ||
query: ` | ||
CREATE TABLE ${tableName} ( | ||
id String, | ||
timestamp DateTime64(3, 'UTC'), | ||
email String, | ||
name Nullable(String) | ||
) | ||
ENGINE MergeTree() | ||
ORDER BY (id) | ||
`, | ||
clickhouse_settings: commandSettings, | ||
}) | ||
|
||
// Here we are assuming that we are getting these rows from somewhere... | ||
const rows = getRows(20_000) | ||
|
||
// Generate the query and insert the values | ||
const insertQuery = ` | ||
INSERT INTO ${tableName} | ||
(id, timestamp, email, name) | ||
VALUES | ||
${rows.map((r) => toInsertValue(r)).join(',')} | ||
` | ||
await client.command({ | ||
query: insertQuery, | ||
clickhouse_settings: commandSettings, | ||
}) | ||
|
||
// Get a few back and print those rows to check what was inserted | ||
const sampleResultSet = await client.query({ | ||
query: `SELECT * FROM ${tableName} ORDER BY rand() LIMIT 3`, | ||
format: 'JSONEachRow', | ||
}) | ||
console.info(`Sample inserted rows:`) | ||
const sampleRows = await sampleResultSet.json<Data[]>() | ||
sampleRows.forEach((row) => { | ||
console.info(row) | ||
}) | ||
|
||
// Close it during your application graceful shutdown | ||
await client.close() | ||
})() | ||
|
||
function getRows(n: number): Data[] { | ||
const now = Date.now() // UNIX timestamp in milliseconds | ||
return [...new Array(n)].map((_, i) => ({ | ||
id: Buffer.from(i.toString(10)).toString('hex'), | ||
timestamp: now - i * 1000, // subtract one second for each row | ||
email: `email${i}@example.com`, | ||
name: i % 2 === 0 ? `Name${i}` : null, // for every second row it is NULL | ||
})) | ||
} | ||
|
||
// Generates something like: | ||
// (unhex('42'), '1623677409123', '[email protected]', 'Name') | ||
// or | ||
// (unhex('144'), '1623677409123', '[email protected]', NULL) | ||
// if name is null. | ||
function toInsertValue(row: Data): string { | ||
const id = `unhex('${row.id}')` | ||
const timestamp = `'${row.timestamp}'` | ||
const email = `'${row.email}'` | ||
const name = row.name === null ? 'NULL' : `'${row.name}'` | ||
return `(${id}, ${timestamp}, ${email}, ${name})` | ||
} |