Mathematical Functions

The everyday numeric toolkit: rounding, roots and powers, logarithms, and the row-wise greatest / least. Operators (+, -, *, /) live on the Arithmetic Operators page; everything here is written as a function call, deliberately — mod(x, 2) reads better than x % 2 for an audience that reads % as "percent".

Rounding: round, floor, ceil, trunc

Four ways to drop decimals, and the difference matters:

round(19.99)      →  20     nearest
floor(19.99)      →  19     always down
ceil(19.01)       →  20     always up
trunc(19.99)      →  19     always toward zero

floor and trunc agree on positive numbers and part company on negatives: floor(-1.5) is -2 (further down), trunc(-1.5) is -1 (toward zero). Use trunc when a value must never be inflated — a fee, a quota — and round when it should simply be nearest.

round and trunc both take an optional number of decimal places: round(x, 2), trunc(x, 2).

One caveat, unavoidable. Half-way rounding of values held as binary floats is decided by the database, not by KQL: 0.15 is stored as 0.149999…, so an engine may round it either way. Where that matters, keep the value a decimal rather than a float.

Logarithms: ln, log10, log

There are three, and the naming is deliberate:

ln(x)          natural logarithm, base e
log10(x)       base 10
log(base, x)   any base — log(2, 8) is 3

There is no one-argument log, and that is not an omission. Every engine accepts log(100) and they do not agree what it means: PostgreSQL and SQLite read it as base 10 and return 2, MariaDB and SQL Server read it as the natural log and return 4.605. A name that means two things cannot be fixed by translating it, only by not offering it — so say which one you want, and ln or log10 will mean the same thing on every database.

With two arguments the base comes first, as it reads aloud: "log base 2 of 8".

greatest and least

These compare across the columns of one row — not down a column:

FIND order_details od
FETCH greatest(od.unit_price, 20.0) at_least_twenty

That is the row's own price, or 20 if the price is lower. The functions that collapse many rows into one are max and min, on the Aggregate Functions page. It is an easy pair to confuse, and the two answer completely different questions.

One caveat, and this one is not cosmetic. As soon as an argument can be blank, the engines part company. With greatest(NULL, 3), PostgreSQL, DuckDB and SQL Server answer 3 — they ignore blanks and take the largest of the rest — while MariaDB, Oracle, Snowflake, SQLite and Trino answer blank, because the SQL standard lets a blank win the moment it appears. Both camps are defensible; PostgreSQL documents its deviation openly.

KQL does not paper over it. Levelling it would mean rewriting one camp's SQL, and with a variable number of arguments that needs a replacement construct rather than a translation — a lot of machinery to hide a difference the query itself can state. So say which you mean: wrap each argument in coalesce with a neutral value to ignore blanks, or keep the blanks in the call and let them win everywhere. If the arguments cannot be blank, none of this applies and the functions are portable.

Division and remainders

mod(x, y) is the remainder. Like /, a zero divisor yields a blank rather than an error — so a report will not fail on one bad row. This is worth knowing because SQL itself is wildly inconsistent here: left alone, three of the supported engines raise an error, three return a blank, and Oracle returns the dividend unchanged, which looks like a real answer and is not one.

Trigonometry

sin, cos, tan, asin, acos and atan are present and take radians. They are not the common case for this audience, and the wider set — hyperbolics, cot, atan2, degrees/radians — is deliberately not included; see math-review.md for the measurements behind that.

abs

abs(value: numeric) → argument-dependent

Absolute value.

Argument Type Description
value numeric the number to take the absolute value of

Sample query:

// abs: absolute value of a number.
FIND orders o
FETCH abs(-17.4) magnitude

Generated SQL

all dialects

-- abs: absolute value of a number.
SELECT
  abs(-17.4) AS magnitude
FROM
 orders o

ceil

ceil(value: numeric) → argument-dependent

Nearest integer greater than or equal to value.

Argument Type Description
value numeric the number to round up

Sample query:

// ceil: round freight up to a whole number.
FIND orders o
FETCH ceil(o.freight) rounded_up

Generated SQL

duckdb · oracle · snowflake · postgresql · mariadb · sqlite · trino

-- ceil: round freight up to a whole number.
SELECT
  ceil(o.freight) AS rounded_up
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
mssql CEILING(o.freight) AS rounded_up

floor

floor(value: numeric) → argument-dependent

Nearest integer less than or equal to value.

Argument Type Description
value numeric the number to round down

Sample query:

// floor: round freight down to a whole number.
FIND orders o
FETCH floor(o.freight) rounded_down

Generated SQL

all dialects

-- floor: round freight down to a whole number.
SELECT
  floor(o.freight) AS rounded_down
FROM
 orders o

round

round(value: numeric) → argument-dependent

Rounds to the nearest whole number.

Argument Type Description
value numeric the number to round

round(value: numeric, scale: INTEGER) → argument-dependent

Rounds to scale decimal places.

Half-way rounding of binary-float inputs is engine-defined: a value such as 0.15 is stored as 0.149999…, so engines may round it up or down.

See: What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Argument Type Description
value numeric the number to round
scale INTEGER number of decimal places to keep

Sample query:

// round: order freight rounded to one decimal place.

FIND orders o
FILTER o.customer_id = 'TOMSP'
FETCH round(o.freight, 1) freight_rounded

Generated SQL

duckdb · oracle · snowflake · mssql · mariadb · sqlite · trino

-- round: order freight rounded to one decimal place.
SELECT
  round(o.freight, 1) AS freight_rounded
FROM
 orders o
WHERE
  o.customer_id = 'TOMSP'

The remaining dialects differ only in this expression:

Dialect Expression
postgresql round(CAST(o.freight AS numeric), 1) AS freight_rounded

trunc

trunc(value: numeric) → argument-dependent

Truncates toward zero — 1.99 becomes 1, and -1.99 becomes -1. Unlike round it never carries, so use it where a value must not be inflated and round where it should be nearest.

Argument Type Description
value numeric the number to truncate

trunc(value: numeric, scale: INTEGER) → argument-dependent

Truncates toward zero, keeping scale decimal places.

Argument Type Description
value numeric the number to truncate
scale INTEGER number of decimal places to keep

Sample query:

// trunc: truncate toward zero, never rounding up.
FIND order_details od
FETCH trunc(od.unit_price) whole_price, trunc(19.99, 1) one_decimal

Generated SQL

duckdb · oracle · snowflake

-- trunc: truncate toward zero, never rounding up.
SELECT
  trunc(od.unit_price) AS whole_price
, trunc(19.99, 1) AS one_decimal
FROM
 order_details od

The remaining dialects differ only in this expression:

Dialect Expression
postgresql , trunc(CAST(19.99 AS numeric), 1) AS one_decimal
sqlite , (CAST(19.99 * pow(10, 1) AS INTEGER) / pow(10, 1)) AS one_decimal

mariadb

-- trunc: truncate toward zero, never rounding up.
SELECT
  TRUNCATE(od.unit_price, 0) AS whole_price
, TRUNCATE(19.99, 1) AS one_decimal
FROM
 order_details od

mssql

-- trunc: truncate toward zero, never rounding up.
SELECT
  ROUND(od.unit_price, 0, 1) AS whole_price
, ROUND(19.99, 1, 1) AS one_decimal
FROM
 order_details od

trino

-- trunc: truncate toward zero, never rounding up.
SELECT
  truncate(od.unit_price) AS whole_price
, truncate(CAST(19.99 AS DECIMAL(38,10)), 1) AS one_decimal
FROM
 order_details od

mod

mod(dividend: numeric, divisor: numeric) → argument-dependent

Remainder of dividend / divisor. A zero divisor yields no value rather than an error, as with /.

Argument Type Description
dividend numeric the number being divided
divisor numeric the number to divide by

Sample query:

// mod: order id modulo 7.
FIND orders o
FETCH mod(o.order_id, 7) bucket

Generated SQL

duckdb · oracle · snowflake · postgresql · mariadb · sqlite · trino

-- mod: order id modulo 7.
SELECT
  mod(o.order_id, NULLIF(7, 0)) AS bucket
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
mssql ((o.order_id) % NULLIF((7), 0)) AS bucket

sign

sign(value: numeric) → INTEGER

Sign of the argument: -1, 0 or 1.

Argument Type Description
value numeric the number whose sign is taken

greatest

greatest(value: ORDERED, more: ORDERED, ...) → argument-dependent

Largest of the arguments, compared across the columns of one row — not to be confused with max, which collapses rows into one.

Not portable when an argument can be NULL. The SQL standard lets NULL win: the result is NULL as soon as any argument is NULL. PostgreSQL deviates and ignores NULL arguments, returning the largest (or smallest) of the rest.

With one NULL argument — greatest(NULL, 3):

Result Dialects
3 — NULL ignored duckdb, mssql, postgresql
NULL — NULL wins mariadb, oracle, snowflake, sqlite, trino

koryki does not paper over this: unifying it would mean rewriting one side, and with a variable number of arguments only through a replacement construct. Make the intent explicit instead — wrap each argument in coalesce with a neutral value to ignore NULLs, or keep them out of the call to let NULL win everywhere.

See: PostgreSQL — GREATEST and LEAST.

Argument Type Description
value ORDERED the values to compare
more ORDERED further values to compare against

Sample query:

// greatest: the larger of two values in the same row.
FIND order_details od
FETCH round(greatest(od.unit_price, 20.0), 2) at_least_twenty

Generated SQL

duckdb · oracle · snowflake · mssql · mariadb · trino

-- greatest: the larger of two values in the same row.
SELECT
  round(greatest(od.unit_price, 20), 2) AS at_least_twenty
FROM
 order_details od

The remaining dialects differ only in this expression:

Dialect Expression
postgresql round(CAST(greatest(od.unit_price, 20) AS numeric), 2) AS at_least_twenty
sqlite round(max(od.unit_price, 20), 2) AS at_least_twenty

least

least(value: ORDERED, more: ORDERED, ...) → argument-dependent

Smallest of the arguments, compared across the columns of one row — the mirror of greatest, and not to be confused with min.

Not portable when an argument can be NULL. The SQL standard lets NULL win: the result is NULL as soon as any argument is NULL. PostgreSQL deviates and ignores NULL arguments, returning the largest (or smallest) of the rest.

With one NULL argument — greatest(NULL, 3):

Result Dialects
3 — NULL ignored duckdb, mssql, postgresql
NULL — NULL wins mariadb, oracle, snowflake, sqlite, trino

koryki does not paper over this: unifying it would mean rewriting one side, and with a variable number of arguments only through a replacement construct. Make the intent explicit instead — wrap each argument in coalesce with a neutral value to ignore NULLs, or keep them out of the call to let NULL win everywhere.

See: PostgreSQL — GREATEST and LEAST.

Argument Type Description
value ORDERED the values to compare
more ORDERED further values to compare against

Sample query:

// least: the smaller of two values in the same row.
FIND order_details od
FETCH least(od.quantity, 10) capped_quantity

Generated SQL

duckdb · oracle · snowflake · mssql · postgresql · mariadb · trino

-- least: the smaller of two values in the same row.
SELECT
  least(od.quantity, 10) AS capped_quantity
FROM
 order_details od

The remaining dialects differ only in this expression:

Dialect Expression
sqlite min(od.quantity, 10) AS capped_quantity

power

power(base: numeric, exponent: numeric) → FLOAT

base raised to the power of exponent.

Argument Type Description
base numeric the number to raise
exponent numeric the power to raise the base to

Sample query:

// power: freight squared.
FIND orders o
FETCH power(o.freight, 2) squared

Generated SQL

duckdb · oracle · snowflake · postgresql · mariadb · sqlite · trino

-- power: freight squared.
SELECT
  power(o.freight, 2) AS squared
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
mssql POWER(CAST(o.freight AS FLOAT), 2) AS squared

Results differ from the other dialects on oracle, postgresql.

sqrt

sqrt(value: numeric) → FLOAT

Square root.

Argument Type Description
value numeric the number to take the square root of

Sample query:

// sqrt: square root of freight.
FIND orders o
FETCH sqrt(o.freight) root

Generated SQL

all dialects

-- sqrt: square root of freight.
SELECT
  sqrt(o.freight) AS root
FROM
 orders o

exp

exp(value: numeric) → FLOAT

Exponential — e raised to value.

Argument Type Description
value numeric the exponent to raise e to

ln

ln(value: numeric) → FLOAT

Natural logarithm — the logarithm to base e.

Argument Type Description
value numeric the number to take the natural logarithm of

Sample query:

// ln: natural logarithm.
FIND orders o
FETCH round(ln(100), 6) natural_log

Generated SQL

duckdb · oracle · snowflake · mariadb · sqlite · trino

-- ln: natural logarithm.
SELECT
  round(ln(100), 6) AS natural_log
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
mssql round(LOG(100), 6) AS natural_log
postgresql round(CAST(ln(100) AS numeric), 6) AS natural_log

log10

log10(value: numeric) → FLOAT

Base-10 logarithm.

Argument Type Description
value numeric the number to take the base-10 logarithm of

Sample query:

// log10: base-10 logarithm.
FIND orders o
FETCH log10(1000) base_ten_log

Generated SQL

duckdb · mssql · postgresql · mariadb · sqlite · trino

-- log10: base-10 logarithm.
SELECT
  log10(1000) AS base_ten_log
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
oracle · snowflake LOG(10, 1000) AS base_ten_log

log

log(base: numeric, value: numeric) → FLOAT

Logarithm of value to the given base: log(2, 8) is 3. The base comes first, as it reads aloud.

Argument Type Description
base numeric the base of the logarithm
value numeric the number to take the logarithm of

Sample query:

// log: logarithm to an explicit base — the base comes first.
// T-SQL takes LOG(value, base), the other way round from every other engine, so this
// pins the value rather than just the rendering: log(2, 8) must be 3 everywhere.
FIND orders o
FETCH log(2, 8) log_base_two

Generated SQL

duckdb · oracle · snowflake · postgresql · mariadb · sqlite · trino

-- log: logarithm to an explicit base — the base comes first.
-- T-SQL takes LOG(value, base), the other way round from every other engine, so this
-- pins the value rather than just the rendering: log(2, 8) must be 3 everywhere.
SELECT
  log(2, 8) AS log_base_two
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
mssql LOG(8, 2) AS log_base_two

sin

sin(value: numeric) → FLOAT

Trigonometric sin (argument in radians).

Argument Type Description
value numeric the angle in radians

Sample query:

// sin: trigonometric sine, argument in radians.
FIND orders o
FETCH round(sin(0), 6) sine_of_zero

Generated SQL

duckdb · oracle · snowflake · mssql · mariadb · sqlite · trino

-- sin: trigonometric sine, argument in radians.
SELECT
  round(sin(0), 6) AS sine_of_zero
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
postgresql round(CAST(sin(0) AS numeric), 6) AS sine_of_zero

cos

cos(value: numeric) → FLOAT

Trigonometric cos (argument in radians).

Argument Type Description
value numeric the angle in radians

Sample query:

// cos: trigonometric cosine, argument in radians.
FIND orders o
FETCH round(cos(0), 6) cosine_of_zero

Generated SQL

duckdb · oracle · snowflake · mssql · mariadb · sqlite · trino

-- cos: trigonometric cosine, argument in radians.
SELECT
  round(cos(0), 6) AS cosine_of_zero
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
postgresql round(CAST(cos(0) AS numeric), 6) AS cosine_of_zero

tan

tan(value: numeric) → FLOAT

Trigonometric tan (argument in radians).

Argument Type Description
value numeric the angle in radians

Sample query:

// tan: trigonometric tangent, argument in radians.
FIND orders o
FETCH round(tan(0), 6) tangent_of_zero

Generated SQL

duckdb · oracle · snowflake · mssql · mariadb · sqlite · trino

-- tan: trigonometric tangent, argument in radians.
SELECT
  round(tan(0), 6) AS tangent_of_zero
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
postgresql round(CAST(tan(0) AS numeric), 6) AS tangent_of_zero

asin

asin(value: numeric) → FLOAT

Trigonometric asin (argument in radians).

Argument Type Description
value numeric the angle in radians

Sample query:

// asin: inverse sine, result in radians.
FIND orders o
FETCH round(asin(1), 6) arcsine_of_one

Generated SQL

duckdb · oracle · snowflake · mssql · mariadb · sqlite · trino

-- asin: inverse sine, result in radians.
SELECT
  round(asin(1), 6) AS arcsine_of_one
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
postgresql round(CAST(asin(1) AS numeric), 6) AS arcsine_of_one

acos

acos(value: numeric) → FLOAT

Trigonometric acos (argument in radians).

Argument Type Description
value numeric the angle in radians

Sample query:

// acos: inverse cosine, result in radians.
FIND orders o
FETCH round(acos(1), 6) arccosine_of_one

Generated SQL

duckdb · oracle · snowflake · mssql · mariadb · sqlite · trino

-- acos: inverse cosine, result in radians.
SELECT
  round(acos(1), 6) AS arccosine_of_one
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
postgresql round(CAST(acos(1) AS numeric), 6) AS arccosine_of_one

atan

atan(value: numeric) → FLOAT

Trigonometric atan (argument in radians).

Argument Type Description
value numeric the angle in radians

Sample query:

// atan: inverse tangent, result in radians.
FIND orders o
FETCH round(atan(0), 6) arctangent_of_zero

Generated SQL

duckdb · oracle · snowflake · mssql · mariadb · sqlite · trino

-- atan: inverse tangent, result in radians.
SELECT
  round(atan(0), 6) AS arctangent_of_zero
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
postgresql round(CAST(atan(0) AS numeric), 6) AS arctangent_of_zero

pi

pi() → FLOAT

Approximate value of π.

Sample query:

// pi: the mathematical constant pi.
FIND orders o
FETCH pi() pi_value

Generated SQL

duckdb · snowflake · mssql · postgresql · mariadb · sqlite · trino

-- pi: the mathematical constant pi.
SELECT
  pi() AS pi_value
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
oracle ACOS(-1) AS pi_value

random

random() → FLOAT

Random value in the range 0.0 <= x < 1.0.

Sample query:

// random: a random value in [0, 1).
FIND orders o
FETCH random() r

Generated SQL

duckdb · postgresql · trino

-- random: a random value in [0, 1).
SELECT
  random() AS r
FROM
 orders o

The remaining dialects differ only in this expression:

Dialect Expression
mariadb RAND() AS r
mssql RAND(CHECKSUM(NEWID())) AS r
oracle DBMS_RANDOM.VALUE AS r
snowflake UNIFORM(0::float, 1::float, RANDOM()) AS r
sqlite (random() / 18446744073709551616.0 + 0.5) AS r