-
Notifications
You must be signed in to change notification settings - Fork 0
Quick SQL Queries
This page collects practical examples to query data directly from the database (PostgreSQL / TimescaleDB) for the following tables:
timeseriesprofilesdetections
The general pattern is always the same:
-
Identify the
datastream_idfrom the"DATASTREAMS"table using:- station (
"THINGS"."NAME") - sensor (
"SENSORS"."NAME") - observed property (
"OBS_PROPERTIES"."NAME") - (optional) filters on
"DATASTREAMS"."PROPERTIES"(e.g.averagePeriod,dataType, etc.)
- station (
- Query the target table (
timeseries,profiles, ordetections) filtering bydatastream_idand by time range.
The table to query depends on the type of data stored in the datastream:
-
timeseries
Regular time series data (one value per timestamp), such as CTD or environmental sensors. -
profiles
Vertical profiles or multi-value observations per timestamp (e.g. ADCP/AWAC profiles), where each timestamp contains multiple depth–value pairs. -
detections
Full detection results from cameras or AI pipelines (e.g. object/species detections), stored as one row per detection.
Note
In this system, full data are stored intimeseriesandprofiles, while aggregated (averaged) data from time series and profiles are stored in theOBSERVATIONStable.See: https://github.com/obsea-upc/mmapi?tab=readme-ov-file#metadata-api-data-types
To build a query you need to know:
-
Station (Thing):
"THINGS"."NAME"(e.g.OBSEA,Lander_AC0D) -
Sensor:
"SENSORS"."NAME"(e.g.SBE37SMP-SN3727115,AWAC-SN5931,IPC608_8BC7_166) -
Observed property (variable / species / class):
"OBS_PROPERTIES"."NAME"(e.g.PRES,CSPD,species_X) - (Optional) If more than one equivalent datastream exists (e.g.
fullvs30min), you must filter by:-
"DATASTREAMS"."NAME", or -
"DATASTREAMS"."PROPERTIES"(JSON)
-
All queries must start by identifying the correct datastream.
- The datastream defines what the data represent (sensor, property, aggregation, pipeline).
- Attributes such as average period, full vs aggregated data, or data type are defined at the datastream level, not in the data tables.
Filtering on the data tables without selecting the correct datastream may return incorrect or incomplete results.
This happens when you use:
-
datastream_id = (SELECT "ID" FROM "DATASTREAMS" ...)and the subquery returns more than one ID.
Solutions:
- Use
IN (...)instead of= (...), or - Add an extra filter to select a single datastream (e.g.
averagePeriod = 30min), or - Use
ORDER BY ... LIMIT 1(only if you are sure which one you want).
In this schema, many core tables use quoted uppercase column names:
-
"DATASTREAMS"."PROPERTIES"(not"properties") -
"DATASTREAMS"."ID", etc.
In hypertables (timeseries, profiles, detections) columns are usually lowercase:
-
datastream_id,timestamp, etc.
Typical query for a time series (e.g. pressure PRES from a sensor at a station):
SELECT *
FROM timeseries
WHERE
datastream_id = (
SELECT "ID"
FROM "DATASTREAMS"
WHERE
"SENSOR_ID" = (SELECT "ID" FROM "SENSORS" WHERE "NAME" = 'SBE37SMP-SN3727115')
AND "THING_ID" = (SELECT "ID" FROM "THINGS" WHERE "NAME" = 'Lander_AC0D')
AND "OBS_PROPERTY_ID" = (SELECT "ID" FROM "OBS_PROPERTIES" WHERE "NAME" = 'PRES')
)
AND "timestamp" BETWEEN '2024-12-01T09:08:11.346Z' AND '2026-01-07T09:08:11.347Z'
ORDER BY 1;In profiles there is no period column.
The distinction between full and 30min is done by selecting the correct
datastream in "DATASTREAMS".
Example: CSPD profiles from AWAC-SN5931 at OBSEA, using the datastream with
averagePeriod = 30min:
SELECT *
FROM "OBSERVATIONS"
WHERE
"DATASTREAM_ID" = (
SELECT "ID" FROM "DATASTREAMS" WHERE
"SENSOR_ID" = (SELECT "ID" FROM "SENSORS" WHERE "NAME" = 'AWAC-SN5931') AND
"THING_ID" = (SELECT "ID" FROM "THINGS" WHERE "NAME" = 'OBSEA') AND
"OBS_PROPERTY_ID" = (SELECT "ID" FROM "OBS_PROPERTIES" WHERE "NAME" = 'CSPD') AND
"PROPERTIES" ->> 'averagePeriod' = '30min') AND
"PHENOMENON_TIME_START" BETWEEN '2024-12-01T09:08:11.346Z' AND '2026-01-07T09:08:11.347Z'
ORDER BY 1;
NOTES
- Aggregated profile data are stored in the
OBSERVATIONStable.- Time information is provided through
PHENOMENON_TIME_STARTandPHENOMENON_TIME_END.
SELECT *
FROM profiles
WHERE
datastream_id IN (
SELECT "ID"
FROM "DATASTREAMS"
WHERE
"SENSOR_ID" = (SELECT "ID" FROM "SENSORS" WHERE "NAME" = 'AWAC-SN5931')
AND "THING_ID" = (SELECT "ID" FROM "THINGS" WHERE "NAME" = 'OBSEA')
AND "OBS_PROPERTY_ID" = (SELECT "ID" FROM "OBS_PROPERTIES" WHERE "NAME" = 'CSPD')
)
AND "timestamp" BETWEEN '2024-12-01T09:08:11.346Z'
AND '2026-01-07T09:08:11.347Z'
ORDER BY 1;Query detections for a camera sensor over a time range.
Because multiple datastreams may exist (different classes/species/pipelines),
IN is usually required.
SELECT *
FROM detections
WHERE
datastream_id IN (
SELECT "ID"
FROM "DATASTREAMS"
WHERE
"SENSOR_ID" = (SELECT "ID" FROM "SENSORS" WHERE "NAME" = 'IPC608_8BC7_166')
)
AND "timestamp" BETWEEN '2023-12-01T09:08:11.346Z'
AND '2024-01-07T09:08:11.347Z'
ORDER BY 1;SELECT *
FROM "OBSERVATIONS"
WHERE
"DATASTREAM_ID" IN (
SELECT "ID" FROM "DATASTREAMS" WHERE
"SENSOR_ID" = (SELECT "ID" FROM "SENSORS" WHERE "NAME" = 'AIPC608UW_10_167')
)AND
"PHENOMENON_TIME_START" BETWEEN '2024-06-26T12:06:41Z' AND '2024-06-26T12:10:01Z'
SELECT *
FROM "DATASTREAMS"
WHERE
"SENSOR_ID" = (SELECT "ID" FROM "SENSORS" WHERE "NAME" = 'SENSOR_NAME');- Always restrict queries by time range.
- Avoid
SELECT *on large time ranges unless strictly necessary. - When exploring data, start with a small time window and increase gradually.