Gerne!
Hier sind die Tabelle mit Anzahl:
# \d device
Table "public.device"
Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+---------
id | uuid | | not null |
device_id | character varying(50) | | not null |
note | character varying(35) | | |
created_at | timestamp with time zone | | not null |
updated_at | timestamp with time zone | | not null |
Indexes:
"device_pkey" PRIMARY KEY, btree (id)
"device_id_uidx" UNIQUE CONSTRAINT, btree (device_id)
# SELECT COUNT(*) FROM device;
count
---------
1527746
(1 row)
# \d measurement
Table "public.measurement"
Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+---------
id | uuid | | not null |
value | character varying(35) | | |
timestamp | timestamp with time zone | | not null |
created_at | timestamp with time zone | | not null |
updated_at | timestamp with time zone | | not null |
device_id | character varying(50) | | not null |
type_id | character varying(3) | | not null |
Indexes:
"measurement_pkey" PRIMARY KEY, btree (id)
"device_timestamp_desc_idx" btree (device_id, "timestamp" DESC)
"measurement_device_type_idx" btree (device_id, type_id)
"measurement_type_id_idx" btree (type_id)
# SELECT COUNT(*) FROM measurement;
count
----------
26463363
(1 row)
# \d measurementconfig
Table "public.measurementconfig"
Column | Type | Collation | Nullable | Default
--------------+--------------------------+-----------+----------+---------
type | character varying(3) | | not null |
is_not_valid | boolean | | not null |
mark_1 | character varying(15) | | not null |
mark_2 | character varying(15) | | not null |
mark_3 | character varying(15) | | not null |
mark_4 | character varying(15) | | not null |
mark_5 | character varying(15) | | not null |
mark_6 | character varying(15) | | not null |
created_at | timestamp with time zone | | not null |
updated_at | timestamp with time zone | | not null |
Indexes:
"measurementconfig_key" PRIMARY KEY, btree (type)
# SELECT COUNT(*) FROM measurementconfig;
count
-------
142
(1 row)
Und das ist eine Beispielabfrage:
EXPLAIN ANALYZE WITH newest_valid_measurement AS (
SELECT DISTINCT ON (m.device_id)
m.device_id,
m.timestamp,
m.type_id,
m.value,
mc.mark_1,
mc.mark_2,
mc.mark_3,
mc.mark_4,
mc.mark_5,
mc.mark_6
FROM measurement m
LEFT JOIN measurementconfig mc ON (m.type_id = mc.type)
WHERE mc.is_not_valid = false
ORDER BY m.device_id, m.timestamp DESC
)
SELECT
d.device_id,
d.note,
m.value
FROM device d
LEFT JOIN newest_valid_measurement m
ON (d.device_id = m.device_id)
WHERE m.mark_6 = 'ok';
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=2384370.46..6192248.52 rows=620 width=26) (actual time=46042.793..139855.102 rows=1211585 loops=1)
-> Subquery Scan on m (cost=2384370.03..6187080.62 rows=620 width=16) (actual time=46039.820..64665.217 rows=1272036 loops=1)
Filter: ((m.mark_6)::text = 'ok'::text)
Rows Removed by Filter: 332837
-> Unique (cost=2384370.03..6185531.74 rows=123910 width=286) (actual time=46039.773..64388.580 rows=1604873 loops=1)
-> Nested Loop (cost=2384370.03..6124053.14 rows=24591440 width=286) (actual time=46039.772..63420.324 rows=14300167 loops=1)
-> Gather Merge (cost=2384369.88..5465423.97 rows=26454428 width=27) (actual time=46039.708..57477.615 rows=26463363 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=2383369.85..2410926.55 rows=11022678 width=27) (actual time=45979.759..52091.054 rows=8821121 loops=3)
Sort Key: m_1.device_id, m_1."timestamp" DESC
Sort Method: external merge Disk: 333312kB
Worker 0: Sort Method: external merge Disk: 333048kB
Worker 1: Sort Method: external merge Disk: 333024kB
-> Parallel Seq Scan on measurement m_1 (cost=0.00..566591.78 rows=11022678 width=27) (actual time=96.415..21116.229 rows=8821121 loops=3)
-> Memoize (cost=0.15..0.17 rows=1 width=9) (actual time=0.000..0.000 rows=1 loops=26463363)
Cache Key: m_1.type_id
Cache Mode: logical
Hits: 26463153 Misses: 210 Evictions: 0 Overflows: 0 Memory Usage: 19kB
-> Index Scan using measurementconfig_key on measurementconfig mc (cost=0.14..0.16 rows=1 width=9) (actual time=0.012..0.012 rows=1 loops=210)
Index Cond: ((type)::text = (m_1.type_id)::text)
Filter: (NOT is_not_valid)
Rows Removed by Filter: 0
-> Index Scan using device_id_uidx on device d (cost=0.43..8.34 rows=1 width=25) (actual time=0.058..0.058 rows=1 loops=1272036)
Index Cond: ((device_id)::text = (m.device_id)::text)
Planning Time: 1.625 ms
JIT:
Functions: 28
Options: Inlining true, Optimization true, Expressions true, Deforming true
Timing: Generation 1.973 ms, Inlining 84.355 ms, Optimization 115.885 ms, Emission 87.947 ms, Total 290.161 ms
Execution Time: 140040.834 ms
(31 rows)
Die CTE aus dem Beispiel in einen MV zum Test eingedost:
## \d+ newest_valid_measurement_mv
Materialized view "public.newest_valid_measurement_mv"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
-----------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
device_id | character varying(50) | | | | extended | | |
timestamp | timestamp with time zone | | | | plain | | |
type_id | character varying(3) | | | | extended | | |
value | character varying(35) | | | | extended | | |
mark_1 | character varying(15) | | | | extended | | |
mark_2 | character varying(15) | | | | extended | | |
mark_3 | character varying(15) | | | | extended | | |
mark_4 | character varying(15) | | | | extended | | |
mark_5 | character varying(15) | | | | extended | | |
mark_6 | character varying(15) | | | | extended | | |
Indexes:
"newest_valid_measurement_mv_device_idx" btree (device_id)
"newest_valid_measurement_mv_mark6_idx" btree (mark_6)
View definition:
SELECT DISTINCT ON (m.device_id) m.device_id,
m."timestamp",
m.type_id,
m.value,
mc.mark_1,
mc.mark_2,
mc.mark_3,
mc.mark_4,
mc.mark_5,
mc.mark_6
FROM measurement m
LEFT JOIN measurementconfig mc ON m.type_id::text = mc.type::text
WHERE mc.is_not_valid = false
ORDER BY m.device_id, m."timestamp" DESC;
Access method: heap
# EXPLAIN ANALYZE SELECT
d.device_id,
d.note,
m.value
FROM device d
LEFT JOIN newest_valid_measurement_mv m
ON (d.device_id = m.device_id)
WHERE m.mark_6 = 'ok';
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
Hash Join (cost=112081.81..174201.91 rows=1214312 width=26) (actual time=7429.966..11942.748 rows=1211544 loops=1)
Hash Cond: ((m.device_id)::text = (d.device_id)::text)
-> Seq Scan on newest_valid_measurement_mv m (cost=0.00..35881.91 rows=1272450 width=16) (actual time=5.410..1678.682 rows=1271950 loops=1)
Filter: ((mark_6)::text = 'ok'::text)
Rows Removed by Filter: 332923
-> Hash (cost=82467.47..82467.47 rows=1531547 width=25) (actual time=7411.494..7411.494 rows=1527746 loops=1)
Buckets: 131072 Batches: 16 Memory Usage: 6449kB
-> Seq Scan on device d (cost=0.00..82467.47 rows=1531547 width=25) (actual time=0.167..6238.127 rows=1527746 loops=1)
Planning Time: 1.348 ms
JIT:
Functions: 12
Options: Inlining false, Optimization false, Expressions true, Deforming true
Timing: Generation 0.482 ms, Inlining 0.000 ms, Optimization 0.353 ms, Emission 4.945 ms, Total 5.780 ms
Execution Time: 11970.090 ms
(14 rows)