tinySQL - Function Examples
Auto-generated from input SQL. Results are shown below; SQL is visible above each result.
tinySQL Function Examples
This file demonstrates all supported SQL functions in tinySQL
Execute these examples to learn the complete SQL dialect
DATE AND TIME FUNCTIONS
Current date and time
| current_timestamp |
| 2026-08-04T15:09:08.3860223+02:00 |
| today |
| 2026-08-04T00:00:00+02:00 |
| CURRENT_TIME |
| 2026-08-04T15:09:08.5999875+02:00 |
NEW: EXTRACT function (alternative to individual functions)
| next_week |
| 2026-08-11T00:00:00+02:00 |
| last_month |
| 2026-07-05T00:00:00+02:00 |
| three_months_later |
| 2026-11-04T00:00:00+01:00 |
| six_months_ago |
| 2026-02-04T00:00:00+01:00 |
NEW: DATE_TRUNC function (truncate to period start)
| start_of_year |
| 2026-01-01T00:00:00+01:00 |
| start_of_quarter |
| 2026-07-01T00:00:00+02:00 |
| start_of_month |
| 2026-08-01T00:00:00+02:00 |
| start_of_week |
| 2026-08-03T02:00:00+02:00 |
| start_of_day |
| 2026-08-04T02:00:00+02:00 |
| start_of_hour |
| 2026-08-04T15:00:00+02:00 |
NEW: EOMONTH function (end of month)
| end_of_current_month |
| 2026-08-31T00:00:00+02:00 |
| end_of_next_month |
| 2026-09-30T00:00:00+02:00 |
| end_of_last_month |
| 2026-07-31T00:00:00+02:00 |
NEW: IN_PERIOD function (period membership checks)
| was_previous_quarter |
| true |
| long_date |
| %A, %B 04, 2026 |
STRING FUNCTIONS
Case conversion
| title_case |
| Hello World From Tinysql |
| joined_with_separator |
| Apple, Banana, Cherry |
| formatted |
| Value: 42, Name: Test |
| formatted_number |
| Pi is approximately 3.14 |
NEW: SPLIT function (returns array)
| fruit_array |
| ["apple","banana","cherry"] |
Split and extract part (existing function)
| quoted_string |
| 'It''s a test' |
NEW: REGEX FUNCTIONS
Pattern matching
| date_with_slashes |
| 2024/11/27 |
| numbers_replaced |
| HelloXWorldX |
FULL-TEXT SEARCH FUNCTIONS
FTS_MATCH — boolean match against a query (term, phrase, boolean, wildcard)
? / _ match one character; * / % match zero or more characters.
| single_char_wildcard |
| true |
FTS_RANK / BM25: relevance score for the same query syntax (higher = more relevant)
FTS_SNIPPET / FTS_HIGHLIGHT: return the text with matches wrapped in markers
| excerpt |
| the quick brown <em>fox</em> |
FTS_WORD_COUNT — number of (stop-word-filtered, stemmed) tokens
(ok)
(ok)
(ok)
(ok)
FTS_SEARCH table-valued function: ranked search across a real table.
With no column arguments it searches the WHOLE ROW (every column, not
just TEXT ones); pass explicit column names to restrict the search.
Repeated calls against an unchanged table reuse a cached tokenization,
so it's cheap for interactive/repeated queries.
| id | title | _fts_score | _fts_rank |
| 1 | Go Programming | 1.8418075983526219 | 1 |
Restrict the search to a single column
(ok)
(ok)
(ok)
(ok)
ROW_TO_TEXT — ad-hoc whole-row substring search inside an ordinary WHERE
clause, combinable with other conditions. Cheaper to set up than
FTS_SEARCH (no ranking, no table function) but also less precise; good
for a quick "search everywhere" filter.
(ok)
(ok)
(ok)
(ok)
(ok)
CONTAINS_ALL / CONTAINS_ANY / CONTAINS_SCORE: a friendlier, case-insensitive
alternative to chaining "ROW_TO_TEXT() LIKE '%term%' AND ROW_TO_TEXT() LIKE
'%other%' ..." for each term. Terms are literal substrings, not patterns —
unlike LIKE, '%' and '_' have no wildcard meaning here.
CONTAINS_ALL — every term must be present (case-insensitive substring match)
| id | description |
| 1 | Widget A2000 - blue, waterproof |
| 3 | Gadget C100 - blue, waterproof, wireless |
CONTAINS_ANY — at least one term must be present
| id | description |
| 2 | Widget B300 - red |
| 3 | Gadget C100 - blue, waterproof, wireless |
CONTAINS_SCORE — count of matched terms (0..N) - use in ORDER BY to rank
rows by how many search terms they contain
| id | description | match_count |
| 3 | Gadget C100 - blue, waterproof, wireless | 3 |
| 1 | Widget A2000 - blue, waterproof | 2 |
| 2 | Widget B300 - red | 0 |
(ok)
NUMERIC FUNCTIONS
Basic math
Logarithms and exponentials
| natural_log |
| 4.605170185988092 |
| ln_value |
| 0.999999327347282 |
| euler_number |
| 2.718281828459045 |
| pi_value |
| 3.141592653589793 |
| arcsine |
| 1.5707963267948966 |
| arccosine |
| 1.5707963267948966 |
| arctangent |
| 0.7853981633974483 |
| atan2_value |
| 0.7853981633974483 |
| degrees_to_radians |
| 3.141592653589793 |
| random_value |
| 0.09334275200814703 |
| rand_value |
| 0.8887558635612671 |
NEW: ARRAY FUNCTIONS
Create arrays
| colors |
| ["red","green","blue"] |
| joined_with_dash |
| a - b - c |
| unique_elements |
| ["a","b","c","d"] |
| sorted_array |
| ["apple","banana","mango","zebra"] |
(ok)
(ok)
(ok)
(ok)
AGGREGATE FUNCTIONS
Create a sample table for aggregate examples
COUNT
| group_id | total_count |
| 1 | 3 |
| 2 | 3 |
| 3 | 2 |
| group_id | non_null_count |
| 1 | 3 |
| 2 | 3 |
| 3 | 2 |
(ok)
(ok)
(ok)
(ok)
(ok)
Basic aggregates (use with GROUP BY in real queries)
SELECT COUNT(*) as total_count FROM table;
SELECT COUNT(column) as non_null_count FROM table;
SELECT SUM(amount) as total FROM table;
SELECT AVG(value) as average FROM table;
SELECT MIN(price) as minimum FROM table;
SELECT MAX(price) as maximum FROM table;
NEW: MIN_BY and MAX_BY - Get value from row with min/max of another column
These are useful for time-series data and finding associated values
Create a sample sales table
Get the product name from the row with the earliest date
Get the product name from the row with the latest date
Get the product name with the lowest amount
Get the product name with the highest amount
Get the amount from the row with the earliest date
Get the amount from the row with the latest date
Aliases ARG_MIN and ARG_MAX are also supported
| cheapest_via_argmin |
| Mouse |
| most_expensive_via_argmax |
| Laptop |
(ok)
(ok)
Clean up
COMPARISON AND RANGE PREDICATES
BETWEEN — inclusive range check, equivalent to (x >= lo AND x <= hi)
BETWEEN also works on dates and strings (lexicographic range for strings)
| alphabetically_between |
| true |
The comparand is evaluated exactly once even for expensive/non-deterministic
expressions, e.g. RANDOM() BETWEEN 0.0 AND 1.0 is always true, never
comparing two different random draws against each other.
| random_is_always_in_unit_range |
| true |
(ok)
(ok)
(ok)
(ok)
(ok)
(ok)
LIMIT / OFFSET
LIMIT ALL means "no limit" (same as omitting LIMIT entirely)
LIMIT/OFFSET accept any constant expression, not just a bare literal
SQL — 2008 standard syntax: OFFSET n ROWS FETCH {FIRST|NEXT} m ROWS ONLY
(ok)
CONDITIONAL AND NULL HANDLING
NULL handling
CRYPTOGRAPHIC AND ENCODING FUNCTIONS
Hashing
| md5_hash |
| 5f4dcc3b5aa765d61d8327deb882cf99 |
| sha1_hash |
| 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 |
| sha256_hash |
| 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 |
| sha512_hash |
| b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86 |
| base64_encoded |
| SGVsbG8gV29ybGQ= |
| base64_decoded |
| Hello World |
JSON FUNCTIONS
JSON extraction
SELECT JSON_GET('{"name":"John","age":30}', 'name') as json_name;
SELECT JSON_EXTRACT('{"user":{"name":"Jane"}}', 'user.name') as nested_value;
TYPE CONVERSION AND INTROSPECTION
Type casting
UTILITY FUNCTIONS
UUID generation
| unique_id |
| 6c69f735-2079-4959-b6c0-e98a5f089bf0 |
| tinysql_version |
| tinySQL 1.0 |
(ok)
(ok)
(ok)
(ok)
(ok)
(ok)
WINDOW FUNCTIONS - FULLY IMPLEMENTED
Window functions operate on a set of rows (window) relative to the current row
Syntax: function() OVER (PARTITION BY ... ORDER BY ... frame_clause)
Create sample table for window function examples
ROW_NUMBER — Assign unique row numbers
| product | amount | row_num |
| Mouse | 25 | 1 |
| Keyboard | 75 | 2 |
| Chair | 200 | 3 |
| Desk | 350 | 4 |
| Laptop | 1200 | 5 |
ROW_NUMBER with PARTITION BY: Restart numbering per partition
| product | category | amount | row_in_category |
| Mouse | Electronics | 25 | 1 |
| Keyboard | Electronics | 75 | 2 |
| Laptop | Electronics | 1200 | 3 |
| Chair | Furniture | 200 | 1 |
| Desk | Furniture | 350 | 2 |
RANK — like ROW_NUMBER, but tied rows share a rank and the next rank
skips ahead by the tie-group size (e.g. 1, 1, 3)
| product | amount | rank_by_amount |
| Laptop | 1200 | 1 |
| Desk | 350 | 2 |
| Chair | 200 | 3 |
| Keyboard | 75 | 4 |
| Mouse | 25 | 5 |
DENSE_RANK — like RANK, but no gaps after ties (e.g. 1, 1, 2)
| product | amount | dense_rank_by_amount |
| Laptop | 1200 | 1 |
| Desk | 350 | 2 |
| Chair | 200 | 3 |
| Keyboard | 75 | 4 |
| Mouse | 25 | 5 |
PERCENT_RANK and CUME_DIST: relative standing within the partition, as a
fraction in [0, 1]
| product | amount | percent_rank | cume_dist |
| Mouse | 25 | 0 | 0.2 |
| Keyboard | 75 | 0.25 | 0.4 |
| Chair | 200 | 0.5 | 0.6 |
| Desk | 350 | 0.75 | 0.8 |
| Laptop | 1200 | 1 | 1 |
NTILE — split the partition into N (approximately) equal-sized buckets
| product | amount | tercile |
| Mouse | 25 | 1 |
| Keyboard | 75 | 1 |
| Chair | 200 | 2 |
| Desk | 350 | 2 |
| Laptop | 1200 | 3 |
LAG — Access previous row value
| product | amount | previous_amount |
| Laptop | 1200 | NULL |
| Mouse | 25 | 1200 |
| Desk | 350 | 25 |
| Keyboard | 75 | 350 |
| Chair | 200 | 75 |
LAG with default value for first row
| product | amount | previous_or_zero |
| Laptop | 1200 | 0 |
| Mouse | 25 | 1200 |
| Desk | 350 | 25 |
| Keyboard | 75 | 350 |
| Chair | 200 | 75 |
LEAD — Access next row value
| product | amount | next_amount |
| Laptop | 1200 | 25 |
| Mouse | 25 | 350 |
| Desk | 350 | 75 |
| Keyboard | 75 | 200 |
| Chair | 200 | NULL |
FIRST_VALUE — Get first value in window
| product | amount | cheapest_product |
| Mouse | 25 | Mouse |
| Keyboard | 75 | Mouse |
| Chair | 200 | Mouse |
| Desk | 350 | Mouse |
| Laptop | 1200 | Mouse |
FIRST_VALUE with PARTITION BY
| product | category | amount | cheapest_in_category |
| Mouse | Electronics | 25 | Mouse |
| Keyboard | Electronics | 75 | Mouse |
| Laptop | Electronics | 1200 | Mouse |
| Chair | Furniture | 200 | Chair |
| Desk | Furniture | 350 | Chair |
LAST_VALUE — Get last value in window (up to current row by default)
| product | amount | most_expensive_so_far |
| Mouse | 25 | Laptop |
| Keyboard | 75 | Laptop |
| Chair | 200 | Laptop |
| Desk | 350 | Laptop |
| Laptop | 1200 | Laptop |
MOVING_SUM — Calculate moving/rolling sum
| product | sale_date | amount | rolling_sum_3days |
| Laptop | 2025-01-15 | 1200 | 1200 |
| Mouse | 2025-01-20 | 25 | 1225 |
| Desk | 2025-01-25 | 350 | 1575 |
| Keyboard | 2025-02-10 | 75 | 450 |
| Chair | 2025-02-15 | 200 | 625 |
MOVING_AVG — Calculate moving/rolling average
| product | sale_date | amount | rolling_avg_3days |
| Laptop | 2025-01-15 | 1200 | 1200 |
| Mouse | 2025-01-20 | 25 | 612.5 |
| Desk | 2025-01-25 | 350 | 525 |
| Keyboard | 2025-02-10 | 75 | 150 |
| Chair | 2025-02-15 | 200 | 208.33333333333334 |
Complex example: LAG with PARTITION BY
| product | category | amount | prev_in_category |
| Laptop | Electronics | 1200 | NULL |
| Mouse | Electronics | 25 | 1200 |
| Keyboard | Electronics | 75 | 25 |
| Desk | Furniture | 350 | NULL |
| Chair | Furniture | 200 | 350 |
(ok)
Clean up
COMPLEX QUERIES COMBINING MULTIPLE FUNCTIONS
Example 1: Date range analysis with period check
| today | month_start | month_end | is_month_to_date | days_into_month |
| 2026-08-04T00:00:00+02:00 | 2026-08-01T00:00:00+02:00 | 2026-08-31T00:00:00+02:00 | true | 3 |
Example 2: String processing with regex
| order_id | username | item_count |
| #12345 | TEST | 3 |
Example 3: Numeric calculations
| circle_area_r5 | angle_degrees | absolute_signed |
| 78.54 | 36.86989764584402 | 100 |
Example 4: Array operations
| first_sorted_unique | sorted_numbers |
| apple | 1 -> 1 -> 3 -> 4 -> 5 -> 9 |
Example 5: Conditional logic with dates
| period_classification |
| Today |
Example 6: NULL handling with coalesce chain
| coalesce_result |
| Final Default |
(ok)
(ok)
(ok)
(ok)
(ok)
PIVOT
PIVOT spreads the distinct values of one column into new output columns,
aggregating another column into each. Every other selected column becomes
an implicit GROUP BY key. Scope: one aggregate function and a static
(literal) value list.
One row per region, one column per category, aliased for clean output names
| region | electronics | furniture |
| East | 100 | 50 |
| West | 200 | 75 |
WHERE filters the source rows before pivoting
| region | electronics |
| East | 100 |
| West | 200 |
COUNT works too, not just SUM
| region | electronics_count | furniture_count |
| East | 1 | 1 |
| West | 1 | 1 |
(ok)
PRACTICAL USE CASES
Use Case 1: Email validation and extraction
| is_valid_email | username | domain |
| true | user | example.com |
Use Case 2: Date-based filtering helper
| in_last_year | in_current_quarter | current_quarter_number |
| true | true | 3 |
(ok)
(ok)
Complex Example: DISTINCT ON + ROW_NUMBER, string concatenation,
column names with spaces, and timestamp helpers.
Use DISTINCT ON to pick the most recent order per customer.
Note: DISTINCT ON keeps the first row per key; ORDER BY controls which row is first.
STRING CONCAT via + operator (also works with CONCAT)
ROW_NUMBER demonstrates window function numbering per PARTITION
TIMESTAMP helpers
| customer | customer name | total | order_date | summary | rn | now_ts | today_date | epoch_zero | ts_epoch |
| alice | Alice A | 200 | 2025-02-05 14:20:00 | alice - 200 | 1 | 2026-08-04T15:09:08.6093595+02:00 | 2026-08-04T00:00:00+02:00 | 1970-01-01T01:00:00+01:00 | 1682899200 |
| bob | Robert B | 50 | 2025-03-01 08:30:00 | bob - 50 | 1 | 2026-08-04T15:09:08.6093595+02:00 | 2026-08-04T00:00:00+02:00 | 1970-01-01T01:00:00+01:00 | 1682899200 |
| carol | Carol C | 300 | 2025-02-20 16:45:00 | carol - 300 | 1 | 2026-08-04T15:09:08.6093595+02:00 | 2026-08-04T00:00:00+02:00 | 1970-01-01T01:00:00+01:00 | 1682899200 |
Show all rows with ROW_NUMBER and LAG for context
| order_id | customer | customer name | order_date | total | rn_asc | prev_total |
| 1 | alice | Alice A | 2025-01-10 09:15:00 | 120 | 1 | NULL |
| 2 | alice | Alice A | 2025-02-05 14:20:00 | 200 | 2 | 120 |
| 3 | bob | Robert B | 2025-01-12 11:05:00 | 75 | 1 | NULL |
| 4 | bob | Robert B | 2025-03-01 08:30:00 | 50 | 2 | 75 |
| 5 | carol | Carol C | 2025-02-20 16:45:00 | 300 | 1 | NULL |
(ok)
Cleanup demo table
Use Case 3: String normalization
| normalized_string |
| HELLO WORLD |
Use Case 4: Array processing from CSV
| color_count | has_green | sorted_unique |
| 4 | true | a,b,c |
Use Case 5: Complex date arithmetic
| mid_year | end_of_quarter | days_in_year |
| 2026-07-01T00:00:00+02:00 | 2026-11-30T00:00:00+01:00 | 364 |
CTE (Common Table Expression) EXAMPLES
Non-recursive CTE: useful for organizing complex subqueries
| sale_date | product |
| 2025-01-01 | A |
Recursive CTE example: generate numbers 1..5
Fibonacci via recursive CTE: generate first 10 Fibonacci numbers
n: index, a: current fib value, b: next fib value
| n | fib_value |
| 0 | 0 |
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 5 | 5 |
| 6 | 8 |
| 7 | 13 |
| 8 | 21 |
| 9 | 34 |
IO AND TRANSFORM FUNCTIONS (safe runnable examples enabled)
GZIP/GUNZIP: Compression functions (safe examples)
| gzip_compressed_length |
| 48 |
| gunzip_roundtrip |
| roundtrip test |
BASE64 encoding/decoding (safe examples)
| base64_encoded_example |
| aGVsbG8gd29ybGQ= |
| base64_decoded_roundtrip |
| hello world |
| file_content |
| [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"},
{"id": 3, "name": "Carol", "email": "carol@example.com"}
]
|
HTTP — Fetch HTTP GET response (30 second timeout)
| http_response |
| {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
} |
Combined example: Read and decompress a gzipped file
| decompressed_content |
| id,name,email
1,Alice,alice@example.com
2,Bob,bob@example.com
3,Carol,carol@example.com
|
Combined example: Fetch and decode base64 data from HTTP
| decoded_api_response |
| {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
} |
| id | name | email |
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Carol | carol@example.com |
| id | name | email |
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Carol | carol@example.com |
TABLE-VALUED FUNCTIONS (TVF)
Note: Table-valued functions are currently in development.
The infrastructure is implemented, but parser integration is pending.
These will be available in a future release:
TABLE_FROM_JSON — Parse JSON array into table
TABLE_FROM_JSON_LINES — Parse JSON Lines (JSONL) format
| id | name | email |
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Carol | carol@example.com |
TABLE_FROM_CSV — Parse CSV data with configurable delimiter
| email | id | name |
| alice@example.com | 1 | Alice |
| bob@example.com | 2 | Bob |
| carol@example.com | 3 | Carol |
Combined example: Query JSON data from HTTP endpoint
Use local demo JSON instead of external HTTP endpoint
| email | id | name |
| alice@example.com | 1 | Alice |
| bob@example.com | 2 | Bob |
| carol@example.com | 3 | Carol |
(ok)
Combined example: Join CSV file with database table
Ensure `users` demo table exists before JOIN examples
ERR: parse error near "ON": unexpected token after statement
ERR: parse error near "ON": unexpected token after statement
ERR: parse error near "ON": unexpected token after statement
Combined example: Join CSV file with database table
SYSTEM CATALOG
Note: System catalog tables are implemented and available via the Go API.
SQL syntax for querying catalog is planned for future release:
List all tables
| name | full_name | columns | rows | is_temp | version | type | tenant | schema |
| users | users | 3 | 0 | false | 0 | TABLE | default | main |
Find columns for a specific table
View all registered functions
ERR: unknown column "return_type"
(ok)
(ok)
(ok)
(ok)
(ok)
(ok)
JOB SCHEDULER
Note: Job scheduler is fully implemented and operational via the Go API.
SQL syntax for job management is planned for future release:
Create a CRON job
Create an INTERVAL job
Create a ONCE job
Enable/disable jobs
Drop a job
PRACTICAL EXAMPLES WITH NEW FUNCTIONS
Example 1: Load and process JSON data from a file
| name | email | id |
| Bob | bob@example.com | 2 |
| Carol | carol@example.com | 3 |
Materialize JSON into a demo table for downstream examples
ERR: parse error near "INTO": unexpected token after statement
Example 2: Fetch and parse CSV from HTTP endpoint
| product | total |
| Mouse | 2 |
| Keyboard | 1 |
| Laptop | 3 |
| Chair | 1 |
| Desk | 1 |
Example 3: Process compressed log files
| id | name | email |
| 2 | Bob | bob@example.com |
| 3 | Carol | carol@example.com |
Example 4: Combine multiple data sources
Example 5: Base64-encoded JSON data
| id | name | email |
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
VECTOR / EMBEDDING FUNCTIONS (RAG Vector Database Support)
tinySQL supports a native VECTOR data type for storing float64 embeddings,
plus a rich set of functions for similarity search, distance computation,
and vector manipulation — enabling RAG (Retrieval-Augmented Generation)
workloads directly in SQL.
---- Vector Creation & Conversion ----
Parse a JSON array into a VECTOR
Serialize a VECTOR back to JSON
---- Vector Introspection ----
Number of dimensions
---- Vector Normalization ----
Normalize to unit length
---- Element-wise Arithmetic ----
Addition
Hadamard (element-wise) product
---- Similarity & Distance Functions ----
Dot product (inner product)
Cosine similarity ∈ [-1, 1] (1 = identical direction)
Cosine distance ∈ [0, 2] (0 = identical)
Generic distance function with selectable metric
---- Vector Manipulation ----
Extract a sub-vector (0-based start, length)
Element-wise average of two vectors
---- Quantization ----
Simulate 8-bit quantization (reduces precision, saves storage)
| quantized_8bit |
| [0,0.5019607843137255,1] |
Simulate 16-bit quantization
| quantized_16bit |
| [0,0.3300068665598535,0.6699931334401464,1] |
---- Random Vector Generation ----
Generate a random unit vector of 5 dimensions
| random_vec |
| [-0.14274490614762214,-0.7160125291820697,-0.523714526457873,0.39095634406149465,-0.19956498099185213] |
Deterministic random vector with a seed
| seeded_vec |
| [0.7546115837811923,0.06083794731271071,-0.24012205373772932,0.6042286795046568,0.06410307295949322] |
(ok)
(ok)
(ok)
(ok)
(ok)
(ok)
---- VECTOR Column Type ----
Tables can have VECTOR columns for storing embeddings
Query embeddings with inline similarity scoring
| title | similarity |
| apple | 1 |
| banana | 0.9938837346736189 |
| elderberry | 0.9701425001453318 |
| cherry | 0 |
| date fruit | 0 |
Filter by similarity threshold
| title | distance |
| apple | 0 |
| banana | 0.006116265326381098 |
| elderberry | 0.029857499854668235 |
---- k-NN Vector Search (Table-Valued Function) ----
VEC_SEARCH(table, column, query_vector, k [, metric [, index]])
Returns the k nearest neighbours with _vec_distance and _vec_rank columns
| id | title | embedding | _vec_distance | _vec_rank | _vec_similarity |
| 1 | apple | [1,0,0] | 0 | 1 | 1 |
| 2 | banana | [0.9,0.1,0] | 0.006116265326381098 | 2 | 0.9938837346736189 |
| 5 | elderberry | [0.8,0.2,0] | 0.029857499854668235 | 3 | 0.9701425001453318 |
Search with L2 distance metric
| _vec_rank | id | title | embedding | _vec_distance | _vec_similarity |
| 1 | 1 | apple | [1,0,0] | 0 | -0 |
| 2 | 2 | banana | [0.9,0.1,0] | 0.1414213562373095 | -0.1414213562373095 |
| 3 | 5 | elderberry | [0.8,0.2,0] | 0.282842712474619 | -0.282842712474619 |
Search with cached approximate ANN indexes for larger RAG tables
| _vec_distance | _vec_similarity | _vec_rank | id | title | embedding |
| 0 | 1 | 1 | 1 | apple | [1,0,0] |
| 0.006116265326381098 | 0.9938837346736189 | 2 | 2 | banana | [0.9,0.1,0] |
| 0.029857499854668235 | 0.9701425001453318 | 3 | 5 | elderberry | [0.8,0.2,0] |
| id | title | embedding | _vec_distance | _vec_similarity | _vec_rank |
| 1 | apple | [1,0,0] | 0 | 1 | 1 |
| 2 | banana | [0.9,0.1,0] | 0.006116265326381098 | 0.9938837346736189 | 2 |
| 5 | elderberry | [0.8,0.2,0] | 0.029857499854668235 | 0.9701425001453318 | 3 |
VEC_WARM(table, column [, metric [, index]]) prebuilds the vector column
cache and the requested ANN index (ivf/hnsw) ahead of time, instead of
paying that one-time O(n log n) build cost on whichever query happens to
run first. Building an HNSW index is real, non-trivial work (each row
insertion does its own approximate-nearest-neighbor search against the
graph built so far) — worth doing explicitly right after a bulk load
(e.g. a nightly re-embed) rather than surprising the first live query with
it. Returns one row describing what was warmed.
| dims | distinct_dims | table_name | row_count | column_name | index_mode | vector_count | metric | excluded_rows |
| 3 | 1 | vec_demo_docs | 5 | embedding | hnsw | 5 | cosine | 0 |
VEC_TOP_K is an alias for VEC_SEARCH
| embedding | _vec_distance | _vec_similarity | id | title | _vec_rank |
| [0,1,0] | 0 | 1 | 3 | cherry | 1 |
| [0.8,0.2,0] | 0.757464374963667 | 0.24253562503633297 | 5 | elderberry | 2 |
(ok)
(ok)
(ok)
(ok)
(ok)
---- RAG Workflow Example ----
1. Create a knowledge base with embeddings
2. Semantic search: find docs closest to a query embedding
| id | content | _vec_distance | _vec_similarity | _vec_rank | embedding |
| 1 | Go is a statically typed language | 0.003718472373113735 | 0.9962815276268863 | 1 | [0.9,0.1,0,0] |
| 2 | Python is dynamically typed | 0.004420608950979843 | 0.9955793910490202 | 2 | [0.8,0.2,0.1,0] |
3. Inline similarity ranking
| content | relevance |
| Go is a statically typed language | 0.9962815276268863 |
| Python is dynamically typed | 0.9955793910490202 |
| Vector search enables AI applications | 0.11426095898687248 |
| Databases store structured data | 0.05747778044001267 |
4. Vector arithmetic for query expansion (average two embeddings)
| expanded_query |
| [0.8500000000000001,0.15000000000000002,0.05,0] |
(ok)
(ok)
(ok)
(ok)
Cleanup demo tables
---- RAG_SEARCH: composed retrieval (one call) ----
RAG_SEARCH(table, vector_column, query_vector, k [, options_json]) composes
VEC_SEARCH + FTS_SEARCH (hybrid reciprocal rank fusion) + RAG_CONTEXT_FROM
(neighbor-chunk expansion) into a single table-valued function, instead of
hand-assembling that pipeline from the three primitives above.
1. Vector-only search (equivalent to VEC_SEARCH, one call)
| doc_id | chunk_index | chunk_text | _vec_distance | _vec_similarity | _vec_rank |
| doc-1 | 0 | TinySQL is a lightweight embeddable SQL engine | 1.1102230246251565e-16 | 0.9999999999999999 | 1 |
| doc-1 | 1 | It supports vector search and full-text search | 0.003718472373113735 | 0.9962815276268863 | 2 |
2. Hybrid vector + BM25 keyword search fused via reciprocal rank fusion
(RRF). key_columns is required in hybrid mode: it's how RAG_SEARCH matches
a row across the independently-fetched vector and text candidate sets.
| doc_id | chunk_index | chunk_text | _rrf_score | _rrf_rank |
| doc-1 | 1 | It supports vector search and full-text search | 0.03252247488101534 | 1 |
| doc-2 | 0 | Full-text search uses BM25 ranking | 0.03200204813108039 | 2 |
| doc-1 | 0 | TinySQL is a lightweight embeddable SQL engine | 0.01639344262295082 | 3 |
3. Hybrid search plus neighbor-chunk context expansion, in one call
(replaces wrapping the hybrid result in RAG_CONTEXT_FROM separately).
| doc_id | chunk_index | chunk_text | _hit_rank | _context_offset | _context_rank |
| doc-1 | 0 | TinySQL is a lightweight embeddable SQL engine | 1 | -1 | 1 |
| doc-1 | 1 | It supports vector search and full-text search | 1 | 0 | 2 |
| doc-1 | 2 | RAG pipelines combine retrieval with generation | 1 | 1 | 3 |
| doc-2 | 0 | Full-text search uses BM25 ranking | 2 | 0 | 4 |
(ok)
(ok)
(ok)
---- HYBRID_SEARCH: one search term, semantic + full-text ----
The query vector is the embedding of the same search term, generated with
the model used to populate the VECTOR column. The PRIMARY KEY is used
automatically to fuse vector and BM25 candidate lists.
| id | content | _vec_similarity | _fts_score | _rrf_score | _rrf_rank |
| 1 | database timeout retry guide | 0.9938837346736189 | 0.9066488893385708 | 0.03278688524590164 | 1 |
| 2 | general systems handbook | 0.9909924304103231 | NULL | 0.016129032258064516 | 2 |
| 3 | unrelated cooking notes | 0.11043152607484652 | NULL | 0.015873015873015872 | 3 |
(ok)
(ok)
(ok)
(ok)
VEC_HYBRID_SEARCH is an alias. Tables without a PRIMARY KEY provide the
identity columns via the optional JSON object:
'{"key_columns":["doc_id","chunk_index"],"index":"hnsw","candidate_k":50}'
GEOSPATIAL FUNCTIONS
Geometry is stored and returned as GeoJSON, either in a plain TEXT/JSON
column or in the dedicated GEOMETRY column type (validated on write and
canonicalized to stable text). Raw four-number coordinate arguments use
(lat, lon, lat, lon); GeoJSON itself stays [lon, lat].
GEO_POINT — build a GeoJSON Point from (lon, lat[, z])
| berlin |
| {"coordinates":[13.405,52.52],"type":"Point"} |
ST_MAKEPOINT / ST_POINT: aliases of GEO_POINT
| berlin | also_berlin |
| {"coordinates":[13.405,52.52],"type":"Point"} | {"coordinates":[13.405,52.52],"type":"Point"} |
GEO_LON / GEO_X: read a Point's longitude. ST_X is the alias.
| lon | lon_alias |
| 13.405 | 13.405 |
GEO_LAT / GEO_Y: read a Point's latitude. ST_Y is the alias.
GEO_DISTANCE — great-circle distance in meters between two points.
HAVERSINE and ST_DISTANCE are aliases.
| a.name | b.name | meters |
| Berlin | Munich | 504307.226096815 |
GEO_DISTANCE also accepts four raw (lat1, lon1, lat2, lon2) numbers
GEO_DWITHIN — true if two points are within a given radius (meters).
ST_DWITHIN is the alias.
GEO_WITHIN_BBOX — true if a point falls inside [minLon, minLat, maxLon, maxLat].
ST_WITHIN_BBOX is the alias.
GEO_BEARING — initial compass bearing (0-360 clockwise from north) from the
first point toward the second. ST_AZIMUTH is the alias.
| a.name | b.name | bearing_deg |
| Berlin | Munich | 195.63069050650938 |
GEO_MIDPOINT — great-circle midpoint between two points. ST_MIDPOINT is the alias.
| a.name | b.name | midpoint |
| Berlin | Munich | {"coordinates":[12.448041477751076,50.33218058133119],"type":"Point"} |
GEO_DESTINATION — project a point along a bearing for a distance (meters).
ST_PROJECT is the alias.
| ten_km_east |
| {"coordinates":[13.552796765540279,52.51990795285367],"type":"Point"} |
GEO_WITHIN_POLYGON — point-in-polygon(-or-multipolygon) test.
ST_WITHIN is the alias; ST_CONTAINS takes the same two arguments reversed
(polygon, point), matching PostGIS's ST_Contains(A, B) = "A contains B".
| point_inside | point_outside |
| true | false |
GEO_POLYGON_AREA — area in square meters, exterior ring minus holes.
ST_AREA is the alias. Accepts a Polygon or MultiPolygon.
| square_meters |
| 7.603723971920487e+07 |
GEO_LENGTH — sum of great-circle segment lengths of a LineString, in meters.
ST_LENGTH is the alias.
| meters |
| 1788.5531141424217 |
GEO_INTERSECTS — true if two geometries (point/line/polygon, any combination)
share at least one point. ST_INTERSECTS is the alias. Respects polygon
holes: a shape nested in another's hole is NOT reported as intersecting.
GEO_DISJOINT — the exact negation of GEO_INTERSECTS. ST_DISJOINT is the alias.
GEO_EQUALS — same coordinates, allowing for a different start vertex,
winding direction, or Polygon-vs-single-part-MultiPolygon wrapping (NOT
full OGC point-set equality -- two differently-vertexized polygons
covering the same area are not detected as equal). ST_EQUALS is the alias.
| same_square_rotated_start |
| true |
GEO_BUFFER — approximate a circular buffer (meters) around a point as a
regular polygon; the optional 3rd argument sets the vertex count (8-256,
default 32). ST_BUFFER is the alias.
| half_km_circle |
| {"coordinates":[[[13.404999999999973,52.524496608029594],[13.407828240070671,52.524154290420434],[13.410225790262416,52.523179466961025],[13.411827597276556,52.52172058096743],[13.412389848573184,52.51999976988185],[13.41182706244581,52.51827902619631],[13.410225033897405,52.51682030292081],[13.407827705239924,52.515845642179514],[13.404999999999973,52.515503391970405],[13.402172294760021,52.515845642179514],[13.399774966102541,52.51682030292081],[13.39817293755425,52.51827902619631],[13.397610151426761,52.51999976988185],[13.39817240272339,52.52172058096743],[13.399774209737643,52.523179466961025],[13.402171759929274,52.524154290420434],[13.404999999999973,52.524496608029594]]],"type":"Polygon"} |
GEO_CONVEX_HULL — convex hull of every vertex in a geometry, as a Polygon.
ST_CONVEXHULL is the alias. Computed in plain lon/lat space (a standard
planar approximation, not a rigorous spherical hull).
| hull |
| {"coordinates":[[[0,0],[2,0],[2,2],[0,2],[0,0]]],"type":"Polygon"} |
GEO_ENVELOPE — a geometry's bounding box as a Polygon (vs. GEO_BBOX's plain
array). ST_ENVELOPE is the alias.
| bbox_polygon |
| {"coordinates":[[[0,0],[3,0],[3,4],[0,4],[0,0]]],"type":"Polygon"} |
GEO_LINE_INTERPOLATE — the point a fraction (0-1) of the way along a
LineString, by actual distance, not vertex count. ST_LINE_INTERPOLATE_POINT
is the alias.
| midway |
| {"coordinates":[13.45,52.56],"type":"Point"} |
GEO_CLIP — Sutherland-Hodgman clip of a geometry to a convex boundary
polygon. ST_CLIP is the alias. Rejects a non-convex boundary unless the
optional 3rd argument (allow_nonconvex) is true -- a best-effort,
not-guaranteed-correct result in that case.
| clipped_to_inner_square |
| {"coordinates":[[[1,3],[1,1],[3,1],[3,3],[1,3]]],"type":"Polygon"} |
GEOSPATIAL — EDITING AND QUALITY
GEO_SIMPLIFY — reduce vertex count. Accepts Douglas-Peucker ('dp', the
default), 'visvalingam-effective', and 'visvalingam-weighted'.
ST_SIMPLIFY is the alias.
| simplified |
| {"coordinates":[[0,0],[2,0],[3,2],[4,2]],"type":"LineString"} |
GEO_BBOX — bounding box as [minLon, minLat, maxLon, maxLat]. ST_BBOX is the alias.
GEO_CENTROID — area/length-weighted centroid of a geometry. ST_CENTROID is the alias.
| centroid |
| {"coordinates":[0,0],"type":"Point"} |
GEO_AFFINE — shift, scale, and rotate a geometry around an anchor (default
anchor: the geometry's own bbox center; here given explicitly as the last
two arguments). ST_AFFINE is the alias.
| rotated_90deg_around_origin |
| {"coordinates":[6.123233995736757e-17,1],"type":"Point"} |
GEO_SMOOTH — Chaikin corner-cutting smoothing, 0-8 iterations. ST_SMOOTH is the alias.
| smoothed |
| {"coordinates":[[0,0],[0.25,0.25],[0.75,0.75],[1.25,0.75],[1.75,0.25],[2,0]],"type":"LineString"} |
GEO_DROP_HOLES — remove every hole from a Polygon/MultiPolygon, keeping only
exterior rings. ST_REMOVE_HOLES is the alias.
| outer_ring_only |
| {"coordinates":[[[-2,-2],[-2,2],[2,2],[2,-2],[-2,-2]]],"type":"Polygon"} |
GEO_CLEAN — remove repeated consecutive vertices and normalize ring closure.
ST_CLEAN is the alias. Rejects a result collapsing below the GeoJSON
minimum vertex count instead of returning invalid output.
| deduplicated |
| {"coordinates":[[0,0],[1,1],[2,2]],"type":"LineString"} |
GEO_SNAP — round coordinates to a grid, then clean. ST_SNAPTOGRID is the alias.
| snapped |
| {"coordinates":[13.405000000000001,52.52],"type":"Point"} |
GEO_IS_VALID — structural GeoJSON check (not full topology validation --
no self-intersection detection). ST_ISVALID is the alias.
| valid | invalid_too_few_points |
| true | false |
(ok)
(ok)
CREATE TABLE ... GEOMETRY: a first-class, validated, canonicalizing column
type (not just TEXT/JSON). A bare number or a Feature/FeatureCollection is
rejected -- a GEOMETRY column holds a Geometry.
| id | shape |
| 1 | {"coordinates":[13.4,52.52],"type":"Point"} |
CAST(x AS GEOMETRY) validates and canonicalizes the same way a column write does
| canonical |
| {"coordinates":[1,2],"type":"Point"} |
(ok)
(ok)
(ok)
(ok)
(ok)
GEOSPATIAL — REGION OPERATIONS, SEARCH, AND CLASSIFICATION
Mapshaper-inspired region-editing verbs and BI-oriented helpers for
turning raw geometry into location-based KPIs and choropleth dashboards.
GEO_DISSOLVE — merge every geometry in a group into one, by cancelling
shared directed edges between adjacent polygons. GEO_UNION_AGG and
ST_UNION are the same operation under aggregate-style names. Correct for
topologically-clean, vertex-aligned adjacent input (e.g. this project's
own output, or real GIS boundary data) -- NOT a general polygon-boolean-
union for overlapping-but-misaligned input.
| region | boundary |
| north | {"coordinates":[[[0,0],[1,0],[2,0],[2,1],[1,1],[0,1],[0,0]]],"type":"Polygon"} |
| south | {"coordinates":[[[10,10],[11,10],[11,11],[10,11],[10,10]]],"type":"Polygon"} |
GEO_UNION_AGG / ST_UNION: aliases of GEO_DISSOLVE
| region | boundary |
| north | {"coordinates":[[[0,0],[1,0],[2,0],[2,1],[1,1],[0,1],[0,0]]],"type":"Polygon"} |
| south | {"coordinates":[[[10,10],[11,10],[11,11],[10,11],[10,10]]],"type":"Polygon"} |
GEO_BBOX_AGG — bounding box across every geometry in a group.
| region | bbox |
| north | [0,0,2,1] |
| south | [10,10,11,11] |
GEO_CENTROID_AGG — (optionally weighted) centroid across a group. The
optional weight combines with GEO_CENTROID's own area/length weighting --
here, a population-weighted centroid of already-area-weighted per-row centroids.
| region | weighted_centroid |
| north | {"coordinates":[1.25,0.5],"type":"Point"} |
| south | {"coordinates":[10.5,10.5],"type":"Point"} |
GEO_SEARCH — an indexed bbox/radius search over a table, backed by a lazy,
per-table grid index invalidated automatically on writes. Exact for Point
columns; for polygon/line columns it indexes by centroid (a large shape
whose edge -- not centroid -- clips into the window is a false negative;
use GEO_INTERSECTS for exact shape overlap).
(ok)
(ok)
(ok)
(ok)
(ok)
(ok)
(ok)
EQUAL_INTERVAL / NATURAL_BREAKS: choropleth classification window
functions, bucketing a KPI column into N legend classes. Quantile
classification needs no new function -- NTILE(n) OVER (ORDER BY kpi)
already does that.
| district | buildings | equal_interval_class | natural_breaks_class | quantile_class |
| a | 5 | 1 | 1 | 1 |
| b | 12 | 1 | 1 | 1 |
| c | 48 | 2 | 2 | 2 |
| d | 51 | 2 | 2 | 2 |
| e | 95 | 3 | 3 | 3 |
(ok)
(ok)
TILE FUNCTIONS
Web Mercator XYZ tile addressing, for working with MBTiles tilesets in
SQL. XYZ (web clients, /{z}/{x}/{y}.png) counts rows from the top; TMS
(what MBTiles stores in tiles.tile_row) counts rows from the bottom.
TILE_X / TILE_Y: the XYZ tile column/row containing (lon, zoom) / (lat, zoom)
TILE_ZXY — the covering tile at one call, including the MBTiles TMS row
| tile |
| {"tile_row":11010,"x":8802,"y":5373,"z":14} |
TILE_FLIP_Y — convert an XYZ row to/from the MBTiles TMS tile_row (its own
inverse). TILE_ROW_TMS is the alias.
TILE_LON / TILE_LAT: the (west, north) edge of a tile
| west_edge | north_edge |
| 13.4033203125 | 52.522905940278065 |
TILE_BBOX — a tile's geographic bounds as [west, south, east, north]
| bounds |
| [13.4033203125,52.50953477032728,13.42529296875,52.522905940278065] |
TILE_QUADKEY / TILE_FROM_QUADKEY: Bing Maps quadkey encoding, and its inverse
| tile |
| {"x":8500,"y":5286,"z":14} |
TILE_PARENT — the containing tile one zoom level up (NULL at zoom 0)
| parent |
| {"x":4401,"y":2686,"z":13} |
TILE_COUNT — how many tiles a fully populated zoom level holds (4^zoom)
TILE_CONTAINS — whether a tile covers a point (edges belong to the tile on
their north/west sides, matching TILE_X/TILE_Y's own assignment)
(ok)
(ok)
A tiles table lookup, converting the client's XYZ row to the MBTiles TMS
row stored on disk:
(ok)