Which waste row to use when the route is unknown

Numbers checked against the live data on

This page shows queries.

Join activity_classification on the waste_stream scheme to see every treatment route a publisher gives for one material, then take the row where is_route_default is true and read route_default_rule in the same breath. For DESNZ paper and board the elected row is landfill at 1.1645 kg CO2e per kg, elected by highest_route, which makes it the stream's maximum and not its average.

List the routes of one stream#

Level 1 of the waste_stream scheme is the publisher's own material label, and level 2 is the row's role: route for one treatment, publisher_mix for the publisher's own average, excluded for a scenario that answers a narrower question.

Show the SQL
SELECT coalesce(f.sub_category, 'no route stated') AS route,
       r.code AS role,
       round(f.value_co2e_normalized::numeric, 4) AS kg_co2e_per_kg,
       f.is_route_default,
       f.route_default_rule,
       f.slug
FROM open_ef.factors_flat f
JOIN open_ef.activity_classification s
  ON s.factor_id = f.factor_id AND s.scheme = 'waste_stream' AND s.level = 1
JOIN open_ef.activity_classification r
  ON r.factor_id = f.factor_id AND r.scheme = 'waste_stream' AND r.level = 2
WHERE f.library = 'desnz'
  AND s.code = 'Paper > Paper and board: paper'
  AND f.is_default_indicator
  AND f.is_latest_activity_year
ORDER BY f.is_route_default DESC, 3 DESC
route role kg_co2e_per_kg is_route_default route_default_rule slug
Landfill route 1.1645 true highest_route ef-desnz-paper_paper_and_board-gb-kg-end_of_life-a42fb8b2
Composting route 0.0090 false NULL ef-desnz-paper_paper_and_board-gb-kg-end_of_life-63f1ae5e
Recycling route 0.0047 false NULL ef-desnz-paper_paper_and_board-gb-kg-end_of_life-fd33df28
Incineration route 0.0047 false NULL ef-desnz-paper_paper_and_board-gb-kg-end_of_life-30448842

To find the stream code for another material, drop the s.code predicate and select s.code for the activity you already have. One stream is one publisher, one material, one unit and one region, and exactly one of its rows carries the flag.

Read the rule, not just the flag#

route_default_rule is NULL on every row that was not elected, and on the elected row it says what kind of number you are holding.

rule the elected row is
publisher_mix The publisher's own average end-of-life row. Use it as a mid estimate
highest_route The highest-emitting eligible route of the stream. A deliberate upper bound: on DESNZ paper it is 250 times the lowest route
sole_route The one eligible row in the stream. There was nothing to choose between

Waste treatment routes and the elected default explains how each rule is decided and which rows can never win.

Never filter on the flag alone#

The flag reaches the view through a join that filters nothing, so it is false, not NULL, on every factor that is not an elected waste row. Adding AND is_route_default to a general query looks harmless and silently throws the rest of factor search away.

Show the SQL
SELECT f.library,
       count(DISTINCT f.factor_id) AS factors,
       count(DISTINCT f.factor_id) FILTER (WHERE f.is_route_default) AS route_defaults,
       count(*) FILTER (WHERE f.is_route_default IS NULL) AS nulls
FROM open_ef.factors_flat f
WHERE f.library IN ('ademe','agribalyse','desnz','epa')
  AND f.is_default_indicator
  AND f.is_latest_activity_year
  AND f.is_top_parent
GROUP BY 1
ORDER BY 1
library factors route_defaults nulls
ademe 2767 40 0
agribalyse 2451 0 0
desnz 1132 42 0
epa 360 61 0

An entire food library comes back with nothing: AGRIBALYSE fans no waste material out by treatment route, so it has no stream and nothing to elect from. The three that do keep a sliver of themselves, 40 rows of ADEME's 2,767, 42 of DESNZ's 1,132 and 61 of EPA's 360. Keep the flag for the last step of a waste query, after you have already narrowed to the stream.

Query the route you know#

When the line does say what happened to the material, ignore the election and ask for that route. The route is the classification sub-category, under category_code = 'waste'.

Show the SQL
SELECT coalesce(f.sub_category_code, 'not stated') AS route_code,
       count(DISTINCT f.factor_id) FILTER (WHERE f.library = 'ademe') AS ademe,
       count(DISTINCT f.factor_id) FILTER (WHERE f.library = 'desnz') AS desnz,
       count(DISTINCT f.factor_id) FILTER (WHERE f.library = 'epa') AS epa
FROM open_ef.factors_flat f
WHERE f.library IN ('ademe','desnz','epa')
  AND f.category_code = 'waste'
  AND f.is_default_indicator
  AND f.is_latest_activity_year
  AND f.is_top_parent
GROUP BY 1
ORDER BY 1
route_code ademe desnz epa
anaerobic_digestion 1 5 20
composting 4 8 15
hazardous_treatment 3 0 0
incineration 17 30 54
landfill 17 39 61
not stated 57 0 0
recycling 29 57 33
wastewater_treatment 1 1 0

Add AND f.sub_category_code = 'landfill' to the query above and you have the landfill rows only.

The 57 ADEME rows that state no route name a material rather than a treatment, and the stream role says which kind each one is.

Show the SQL
SELECT r.code AS role,
       f.system_boundary,
       count(DISTINCT f.factor_id) AS factors
FROM open_ef.factors_flat f
JOIN open_ef.activity_classification r
  ON r.factor_id = f.factor_id AND r.scheme = 'waste_stream' AND r.level = 2
WHERE f.library = 'ademe'
  AND f.category_code = 'waste'
  AND f.sub_category_code IS NULL
  AND f.is_default_indicator
  AND f.is_latest_activity_year
  AND f.is_top_parent
GROUP BY 1, 2
ORDER BY 3 DESC
role system_boundary factors
publisher_mix end_of_life 50
excluded end_of_life 7

Fifty are a publisher's average end-of-life row for the material, the kind publisher_mix elects from, and seven are an average with recycling taken out. All 57 are end_of_life. ADEME's negative avoided-credit rows are not among them: they are reported beside the treatment row they credit rather than standing alone, so is_top_parent keeps them out of the counts in this section. Drop that filter and they come back, under the same stream and the same roles.

Watch out#

Watch out. Use a LEFT JOIN when you attach the election to a list of waste factors. A stream whose rows are all ineligible has no elected row at all, and an inner join drops those lines without a word. No public stream is in that state today, but the model allows it.

Watch out. Log the assumption. When you take the elected row because the route is unknown, record the slug you used and the rule that chose it. A highest_route number is defensible as a conservative estimate and indefensible as a measurement, and a later release can move the flag to a different row of the same stream.

Note. Neither column is an API filter, an export column or a factor search facet. This is a SQL-only route into the data; on the portal you pick the route row yourself.