SQL Commands
Run SQL queries across distributed operator nodes, with time-series functions, casting, and formatting options.
At its core, AnyLog is intended to provide access to data from devices and sensors in real-time — transforming small sections of a factory line or a power plant into a unified data lake at the edge. Each AnyLog agent connects to the appropriate logical database and a shared blockchain network; one node on that network, the Query Node, holds a logical database called system_query and uses blockchain metadata to locate the relevant Operator nodes, distribute the query, and assemble a unified result — letting a query behave as though all the data resides locally on a single machine.
Related Topics:
- Data Storage
- Data Discovery
- Query Blob Data
SQL + run client ()
In the previous sections we had talked about the idea of run client and
how to specify the proper IP and ports for sending requests between nodes. Except for the sql command,
the content in run client must be filled in.
Invalid: The example is invalid because run client does not know where to send the request.
run client () get processes
Valid: This example is valid because run client knows where to send the request — either via the actual
IP:Ports or the blockchain.
run client (blockchain get master bring.ip_port) get processes
run client (10.0.0.14:32148,10.0.0.15:32158) get processes
However, when you specify run client () sql ... the platform knows that this is a SQL request and is able to utilize the
blockchain in order to locate where the data resides.
When using REST, the -H "destination: network" replaces run client ().
The sql Command
The sql command is able to run SQL commands locally or across the network if initiated by run client ().
Before defining the actual SELECT statement, and since this is a distributed network, the command contains a
set of options that allow it to better unify the result set — whether in terms of formatting or timezone.
| Key | Values | Default | Description |
|---|---|---|---|
format |
json / table / json:list / json:output |
json |
Output format |
timezone |
utc / local / pt / mt / ct / et / tz name |
local |
Timezone for timestamps when data is returned |
stat |
true / false |
true |
Include processing statistics |
max_time |
seconds | — | Cap query execution time |
drop |
true / false |
true |
Drop local output table after query |
dest |
stdout / rest / dbms / file |
dynamic | Destination for result set |
include |
dbms.table |
— | Treat a differently-named remote table as the queried table |
extend |
column list | — | Include node variables (e.g. @ip, @port) in results |
nodes |
main / all |
main |
HA: main uses designated primary operators; all uses round-robin |
committed |
true / false |
false |
HA: only return data confirmed synced across cluster nodes |
sql [db name] format=table and stat=true and timezone=utc and extend=(+node_name) "SELECT ...."
Supported SQL
AnyLog is a time-series focused platform, providing SQL support across different database types as though they are unified. While we support much of the standard functionality, we also support extended functionality or lack certain functionality that may be of interest. Please reach out to info@anylog.network if you are looking to get support for something that’s not there.
Supported Aggregation functions:
MINMAXAVG- since this is a distributed network, when executingSELECT AVGthe query node asks each operator to bring it theSUMandCOUNTfor said column, and then aggregates the results into a unified average value.SUMCOUNTCOUNT(DISTINCT())RANGE- the difference between the min and max value of a column
In addition, AnyLog also supports WHERE, GROUP BY, ORDER BY and LIMIT.
When doing
SELECT *orSELECT COUNT(*)the Query node utilizes the table policy in the blockchain to convert*into actual columns before forwarding the request across the network. As such, if the table policy is not on the blockchain then the query could fail not because it doesn’t see where the data resides, but rather it fails to define how the data is organized.
Cast data & Formatting Options
Apply casts to projected columns using :::
| Cast | Description |
|---|---|
float(x) |
Float rounded to x decimal places. Add % before x for comma-separated thousands |
int |
Cast to integer |
str |
Cast to string |
ljust(x) |
Left-justified string, x bytes wide |
rjust(x) |
Right-justified string, x bytes wide |
format(type) |
Apply formatting (see below) |
datetime(code) |
Parse datetime and reformat using format code |
function(expr) |
Evaluate expression per row (can reference other columns as [col_name]) |
lstrip / rstrip |
Remove leading/trailing spaces |
timediff |
Time difference vs now() or a datetime string, returned as HH:MM:SS.f |
timezone |
Override query timezone for this column |
replace(old by new) |
Replace a substring once |
Type Description :,Comma as thousands separator :bBinary :xHex :oOctal :eScientific notation :.3fFloat with 3 decimal places :08.3fFloat with zero-padded width
# Single Casting
SELECT reading_time, speed::float(2) FROM performance WHERE reading_time >= NOW() -3d
# Multiple Casting
SELECT reading_time, speed::float(2)::format(:,) FROM performance WHERE reading_time >= NOW() -3d
Time series optimized queries
In addition to the standard SQL functions mentioned above, AnyLog also has its own time-series optimization windowing functions.
periodwhich brings the last before a given timestampincrementswhich aggregate the data into buckets of time.
Note: the two functions cannot co-exist on the same query at this time.
period
The period function is a component of the WHERE condition that allows you to find the last occurrence of data
before a certain date.
period(time-interval, units, date-time, date-column, filter-criteria)
In most SQL languages in order to find the last occurrence of data the user usually needs to (slowly) increment the WHERE condition. For example, if data hasn’t come in for over 12 hours the user is not aware of this, they’d probably run something like this:
-- first iteration
WHERE timestamp <= NOW() - 1 hour
-- second iteration
WHERE timestamp <= NOW() - 12 hours
-- third iteration: data is found
WHERE timestamp <= NOW() - 1 day
The period function does that for the user automatically.
Examples: Get the last 10 rows from July 19 2026
AL anylog-query +> <run client () sql cos format=table
"SELECT
timestamp, value
FROM
pv
WHERE
period(hour, 12, '2026-07-20 00:00:00', timestamp)
ORDER BY timestmap DESC
LIMIT 10;">
timestamp value
-------------------------- ------------------
2026-07-19 23:59:57.595244 1.9200000762939453
2026-07-19 23:59:51.726978 1.9200000762939453
2026-07-19 23:59:46.030184 1.9200000762939453
2026-07-19 23:59:40.180862 1.9200000762939453
2026-07-19 23:59:34.552881 1.9200000762939453
2026-07-19 23:59:28.864503 1.9200000762939453
2026-07-19 23:59:23.043708 1.9200000762939453
2026-07-19 23:59:17.386626 1.9200000762939453
2026-07-19 23:59:11.535526 1.9200000762939453
2026-07-19 23:59:05.795202 1.9200000762939453
{"Statistics":[{"Count": 10,
"Time":"00:00:00",
"Nodes": 1}]}
increments
The increments function buckets or groups content into interval-based result sets.
increments(time-unit, interval, date-column)
Valid time units:
second,minute,hours,days,weeks,month,year
Unlike the period function, this is something standard SQL can do natively — however, the syntax differs per SQL
dialect:
-- Postgres
SELECT date_trunc('hour', ts) AS hour_bucket, AVG(value)
FROM readings
WHERE ts >= NOW() - INTERVAL '3 hours'
GROUP BY hour_bucket
ORDER BY hour_bucket;
-- SQLite
SELECT strftime('%Y-%m-%d %H:00:00', ts) AS hour_bucket, AVG(value)
FROM readings
WHERE ts >= datetime('now', '-3 hours')
GROUP BY hour_bucket
ORDER BY hour_bucket;
By defining increments as a way to divide a time range into fixed buckets and aggregate per bucket, AnyLog is
able to accomplish this across its network no matter the database engine underneath.
Example — Get 5-minute increments of data over the last 1 hour (from now)
AL anylog-query +> <run client () sql cos format=table
"SELECT
increments(minute, 5, timestamp), MIN(timestamp)::ljust(19), MAX(timestamp)::ljust(19),
MIN(value)::float(3), MAX(value)::float(3), AVG(value)::float(3), COUNT(*)
FROM
pv
WHERE
timestamp >= NOW() - 1 hour;">
min(timestamp) max(timestamp) min(value) max(value) avg(value) count(*)
------------------- ------------------- ---------- ---------- ---------- --------
2026-07-28 16:26:52 2026-07-28 16:29:58 3.87 3.87 3.87 34
2026-07-28 16:30:03 2026-07-28 16:34:57 2.88 3.88 3.426 53
2026-07-28 16:35:03 2026-07-28 16:39:56 2.88 2.89 2.88 48
2026-07-28 16:40:01 2026-07-28 16:44:54 1.92 2.88 2.672 52
2026-07-28 16:45:00 2026-07-28 16:49:56 1.91 1.92 1.919 52
2026-07-28 16:50:02 2026-07-28 16:54:54 1.91 1.92 1.919 52
2026-07-28 16:55:00 2026-07-28 16:59:56 1.91 1.92 1.919 52
2026-07-28 17:00:01 2026-07-28 17:04:59 1.91 1.92 1.919 53
2026-07-28 17:05:05 2026-07-28 17:09:59 1.91 1.92 1.919 52
2026-07-28 17:10:04 2026-07-28 17:14:59 1.91 1.92 1.919 52
2026-07-28 17:15:05 2026-07-28 17:19:58 1.91 1.92 1.919 52
2026-07-28 17:20:03 2026-07-28 17:24:56 1.91 1.92 1.919 52
2026-07-28 17:25:02 2026-07-28 17:26:43 1.91 1.92 1.918 18
{"Statistics":[{"Count": 13,
"Time":"00:00:00",
"Nodes": 1}]}
Via REST
For queries sent through an application (via REST) it is recommended to use format=json:list and stat=false.
That way the content returns as a list of JSONs that are machine-readable without extra statistics or complex formatting.
curl -X GET 127.0.0.1:32349 \
-H "command: sql mydb format=table SELECT timestamp, value FROM rand_data WHERE period(minute, 1, now(), timestamp)" \
-H "User-Agent: AnyLog/1.23" \
-H "destination: network" \
-w "\n"
curl -X GET 127.0.0.1:32349 \
-H "Content-Type: application/json" \
-d '{"command": "sql mydb format=table SELECT timestamp, value FROM rand_data WHERE period(minute, 1, now(), timestamp)", "AnyLog-Agent": "AnyLog/1.23", "destination": "network"}' \
-w "\n"
Query Examples
Some more SQL examples with AnyLog.
# Last minute of readings
run client () sql mydb format=table "select max(timestamp), avg(value) from ping_sensor where period(minute, 1, now(), timestamp)"
# 5-minute trends over a range
run client () sql mydb format=table "SELECT increments(minute, 5, timestamp), max(timestamp), avg(value) from ping_sensor where timestamp >= '2019-06-01' and timestamp < '2019-09-29'"
# Speed as formatted int with thousands separator
run client () sql lsl_demo "select reading_time, speed::int::format(':,') from performance where reading_time >= now() -3d"
# Time difference from last reading to now
run client () sql orics stat=false "select max(insert_timestamp)::timediff(now()) as time_diff FROM r_50"
# Per-second buckets with timezone and datetime formatting
<run client () sql new_company format=table and stat=false "
SELECT increments(second, 1, timestamp),
min(timestamp)::timezone(local)::datetime('%d-%b-%Y %H:%M') as min_ts,
max(timestamp)::timezone(local)::datetime('%d-%b-%Y %H:%M') as max_ts,
min(value), avg(value)::float(3), max(value)
FROM rand_data
WHERE timestamp >= '2024-12-20 00:00:00' AND timestamp <= '2025-01-10 23:59:59'
ORDER BY min_ts DESC LIMIT 1">