I try to create a PostGIS view in which I want the number of features having an attribute date < 3 years from current date, and the total number of features, to be able to display a pie chart in QGIS. The features needs to be grouped by town (commune in French) and sometimes by a smaller town division called "tournee" in this case.
I have a materialized view which contains the towns and tournees, with the geometry.
The features I need to count are in two different tables.
My first try is working but is not efficient when I display the view in QGIS.
The view is created with the following SQL :
CREATE OR REPLACE VIEW deci.v_pei_avancee_rop_communes_tournees
AS
-- Get feature from the two soruce table (features to count)
WITH t1 AS (
SELECT pei_raccorde.code_insee,
pei_raccorde.commune,
pei_raccorde.numero_tournee,
pei_raccorde.date_suppression,
pei_raccorde.date_reco_ops
FROM deci.pei_raccorde
UNION ALL
SELECT pei_na_non_raccorde.code_insee,
pei_na_non_raccorde.commune,
pei_na_non_raccorde.numero_tournee,
pei_na_non_raccorde.date_suppression,
pei_na_non_raccorde.date_reco_ops
FROM deci.pei_na_non_raccorde
ORDER BY code_insee, numero_tournee
)
-- Create view attributes
SELECT
-- view id
row_number() OVER () AS id,
-- town/subdivision name
CASE
-- if town does not have a subdivision
WHEN t1.numero_tournee IS NULL
-- name is the town name
THEN t1.commune::text
-- else concat town name and subdivision name
ELSE concat(t1.commune, '-Tournée ', t1.numero_tournee)
END AS commune_tournee,
-- count the features when date_reco_ops is < 3 years before current year
count(
CASE
WHEN (date_part('year'::text, now()) - date_part('year'::text, t1.date_reco_ops)) < 3::double precision THEN 1
ELSE NULL::integer
END) AS nb_controles
-- count all features
count(*) AS nb_pei,
-- geometry : get from materialized view ct
ct.geom
FROM t1
-- join with materialized view v_communes_tournees on fields code_insee and numero_tournee
JOIN deci.v_communes_tournees ct ON ct.code_insee::text = t1.code_insee::text AND NOT ct.numero_tournee::text IS DISTINCT FROM t1.numero_tournee::text
-- get only features where date_suppression is not filled (=null)
WHERE t1.date_suppression IS NULL
GROUP BY t1.code_insee, t1.commune, t1.numero_tournee, ct.geom
ORDER BY t1.code_insee, t1.numero_tournee;
I have the following indexes :
- geom (gist) on the materialized view
- code_insee and numero_tournee (btree) on the materialized view and the 2 source tables
In QGIS the materialized view is fast to display. The deci.v_pei_avancee_rop_communes_tournees view is slow to display (even withou the pie chart).
Add JSON QUERY PLAN :
[
{
"Plan": {
"Node Type": "WindowAgg",
"Parallel Aware": false,
"Actual Rows": 389,
"Actual Loops": 1,
"Plans": [
{
"Node Type": "Aggregate",
"Strategy": "Sorted",
"Partial Mode": "Simple",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Actual Rows": 389,
"Actual Loops": 1,
"Group Key": [
"pei_raccorde.code_insee",
"pei_raccorde.numero_tournee",
"pei_raccorde.commune",
"ct.geom"
],
"Plans": [
{
"Node Type": "Incremental Sort",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Actual Rows": 9610,
"Actual Loops": 1,
"Sort Key": [
"pei_raccorde.code_insee",
"pei_raccorde.numero_tournee",
"pei_raccorde.commune",
"ct.geom"
],
"Presorted Key": [
"pei_raccorde.code_insee"
],
"Full-sort Groups": {
"Group Count": 109,
"Sort Methods Used": [
"quicksort"
],
"Sort Space Memory": {
"Average Sort Space Used": 517,
"Peak Sort Space Used": 522
}
},
"Pre-sorted Groups": {
"Group Count": 111,
"Sort Methods Used": [
"quicksort"
],
"Sort Space Memory": {
"Average Sort Space Used": 1375,
"Peak Sort Space Used": 1560
}
},
"Plans": [
{
"Node Type": "Merge Join",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Join Type": "Inner",
"Actual Rows": 9610,
"Actual Loops": 1,
"Inner Unique": false,
"Merge Cond": "((ct.code_insee)::text = (pei_raccorde.code_insee)::text)",
"Join Filter": "(NOT ((ct.numero_tournee)::text IS DISTINCT FROM (pei_raccorde.numero_tournee)::text))",
"Rows Removed by Join Filter": 34024,
"Plans": [
{
"Node Type": "Sort",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Actual Rows": 426,
"Actual Loops": 1,
"Sort Key": [
"ct.code_insee"
],
"Sort Method": "quicksort",
"Sort Space Used": 2132,
"Sort Space Type": "Memory",
"Plans": [
{
"Node Type": "Seq Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Relation Name": "v_communes_tournees",
"Alias": "ct",
"Actual Rows": 426,
"Actual Loops": 1
}
]
},
{
"Node Type": "Materialize",
"Parent Relationship": "Inner",
"Parallel Aware": false,
"Actual Rows": 43635,
"Actual Loops": 1,
"Plans": [
{
"Node Type": "Sort",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Actual Rows": 9611,
"Actual Loops": 1,
"Sort Key": [
"pei_raccorde.code_insee",
"pei_raccorde.numero_tournee"
],
"Sort Method": "quicksort",
"Sort Space Used": 1099,
"Sort Space Type": "Memory",
"Plans": [
{
"Node Type": "Append",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Actual Rows": 9625,
"Actual Loops": 1,
"Subplans Removed": 0,
"Plans": [
{
"Node Type": "Seq Scan",
"Parent Relationship": "Member",
"Parallel Aware": false,
"Relation Name": "pei_raccorde",
"Alias": "pei_raccorde",
"Actual Rows": 9569,
"Actual Loops": 1,
"Filter": "(date_suppression IS NULL)",
"Rows Removed by Filter": 78
},
{
"Node Type": "Seq Scan",
"Parent Relationship": "Member",
"Parallel Aware": false,
"Relation Name": "pei_na_non_raccorde",
"Alias": "pei_na_non_raccorde",
"Actual Rows": 56,
"Actual Loops": 1,
"Filter": "(date_suppression IS NULL)",
"Rows Removed by Filter": 0
}
]
}
]
}
]
}
]
}
]
}
]
}
]
},
"Triggers": []
}
]
explain analyze
on your queryrow_number() OVER () AS id
is wrong. You must include anorder by
within theover
clause, else there is no guarantee that the same row will have the same ID in subsequent calls (like between fetching the shape and the attributes)