@mxpicture/tesla-superchargers-mate 0.1.62 → 0.1.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/db/migrations/20260907181456_refs_precision_checks.sql +183 -0
- package/db/schema.sql +31 -18
- package/package.json +2 -2
- package/sql/views.sql +61 -10
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
-- pic_refs: the precision `hours` needs now that pic_refs_v divides by it, and
|
|
2
|
+
-- two invariants stated as CHECK constraints.
|
|
3
|
+
--
|
|
4
|
+
-- (1) hours: 2 -> 6 dp. It was a display value, which is why 20260903171045
|
|
5
|
+
-- moved mean_draw_kw off it and onto the raw interval. It stops being one
|
|
6
|
+
-- here: pic_refs_v's gross_mean_draw_kw becomes
|
|
7
|
+
-- gross_dropped_kwh / gross_hours, so `hours` is a divisor one level up --
|
|
8
|
+
-- and rounding a divisor is what caused that defect in the first place. At
|
|
9
|
+
-- 2 dp the resulting error on gross_mean_draw_kw reaches 5.9455 kW and two
|
|
10
|
+
-- sub-18-second refs round to a zero divisor and vanish; at 6 dp the error
|
|
11
|
+
-- is 0.0020 kW and nothing vanishes. It also keeps a row self-consistent:
|
|
12
|
+
-- at 2 dp, 354 of 700 rows would show a gross_mean_draw_kw that does not
|
|
13
|
+
-- reconcile with the gross_hours printed beside it.
|
|
14
|
+
--
|
|
15
|
+
-- Nothing else moves. mean_draw_kw at 3 dp (1 W), cost_per_kwh at 4 (the
|
|
16
|
+
-- 0.08 EUR/kWh band the column exists to resolve) and the rest are terminal
|
|
17
|
+
-- display values that feed no computation, and each scale was chosen for
|
|
18
|
+
-- what its column measures.
|
|
19
|
+
--
|
|
20
|
+
-- (2) efficiency > 0. Four generation expressions carried NULLIF(efficiency, 0)
|
|
21
|
+
-- to keep a zero out of a divisor. All that could ever do was turn a broken
|
|
22
|
+
-- row into a silent NULL; stating the invariant once, checked on write, is
|
|
23
|
+
-- the same guarantee and a better failure. A car with efficiency = 0 now
|
|
24
|
+
-- fails the build loudly, which is consistent with what already happens for
|
|
25
|
+
-- a NULL efficiency (review Md-6). The NULLIF then comes out of those four
|
|
26
|
+
-- expressions, each keeping its round() scale exactly as it was.
|
|
27
|
+
--
|
|
28
|
+
-- (3) end_date >= start_date. An interval that runs backwards gives a negative
|
|
29
|
+
-- `hours` and a mean_draw_kw whose sign means the opposite of what it says.
|
|
30
|
+
-- Both CHECKs are validated against every existing row on ADD, and both
|
|
31
|
+
-- return zero rows on the dev history; re-run the two SELECTs in section 8
|
|
32
|
+
-- of the plan against production before applying.
|
|
33
|
+
--
|
|
34
|
+
-- The order below is forced, not stylistic. PG18 refuses SET EXPRESSION on a
|
|
35
|
+
-- VIRTUAL generated column while the table carries any CHECK constraint
|
|
36
|
+
-- (0A000), so every expression has to be rewritten before the CHECKs go on --
|
|
37
|
+
-- and a later migration that wants to change one of these columns has to drop
|
|
38
|
+
-- the two CHECKs, rewrite, and add them back. migrate:down runs the same
|
|
39
|
+
-- sequence in reverse for the same reason.
|
|
40
|
+
--
|
|
41
|
+
-- Every column touched is VIRTUAL, so SET EXPRESSION is a catalog edit and none
|
|
42
|
+
-- of this rewrites the table. The two CHECKs do scan it once.
|
|
43
|
+
--
|
|
44
|
+
-- docs/plans/2026-09-07_pic-refs-gross-ratios.md, sections 5 and 8.
|
|
45
|
+
-- migrate:up
|
|
46
|
+
ALTER TABLE pic_refs
|
|
47
|
+
ALTER COLUMN hours
|
|
48
|
+
SET
|
|
49
|
+
EXPRESSION AS (
|
|
50
|
+
round(
|
|
51
|
+
EXTRACT(
|
|
52
|
+
epoch
|
|
53
|
+
FROM
|
|
54
|
+
(end_date - start_date)
|
|
55
|
+
) / 3600.0,
|
|
56
|
+
6
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
ALTER TABLE pic_refs
|
|
61
|
+
ALTER COLUMN mean_draw_kw
|
|
62
|
+
SET
|
|
63
|
+
EXPRESSION AS (
|
|
64
|
+
round(
|
|
65
|
+
(start_range_km - end_range_km) * efficiency / NULLIF(
|
|
66
|
+
EXTRACT(
|
|
67
|
+
epoch
|
|
68
|
+
FROM
|
|
69
|
+
(end_date - start_date)
|
|
70
|
+
) / 3600.0,
|
|
71
|
+
0::NUMERIC
|
|
72
|
+
),
|
|
73
|
+
3
|
|
74
|
+
)
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
ALTER TABLE pic_refs
|
|
78
|
+
ALTER COLUMN dropped_kwh
|
|
79
|
+
SET
|
|
80
|
+
EXPRESSION AS ((start_range_km - end_range_km) * efficiency);
|
|
81
|
+
|
|
82
|
+
ALTER TABLE pic_refs
|
|
83
|
+
ALTER COLUMN kwh_per_real_1km
|
|
84
|
+
SET
|
|
85
|
+
EXPRESSION AS (
|
|
86
|
+
round(
|
|
87
|
+
(start_range_km - end_range_km) * efficiency / NULLIF(real_km, 0::NUMERIC),
|
|
88
|
+
6
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
ALTER TABLE pic_refs
|
|
93
|
+
ALTER COLUMN cost_per_kwh
|
|
94
|
+
SET
|
|
95
|
+
EXPRESSION AS (
|
|
96
|
+
CASE
|
|
97
|
+
WHEN start_range_km < end_range_km THEN NULL
|
|
98
|
+
ELSE round(
|
|
99
|
+
cost / NULLIF(
|
|
100
|
+
(start_range_km - end_range_km) * efficiency,
|
|
101
|
+
0::NUMERIC
|
|
102
|
+
),
|
|
103
|
+
4
|
|
104
|
+
)
|
|
105
|
+
END
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
ALTER TABLE pic_refs
|
|
109
|
+
ADD CONSTRAINT pic_refs_efficiency_positive CHECK (efficiency > 0);
|
|
110
|
+
|
|
111
|
+
ALTER TABLE pic_refs
|
|
112
|
+
ADD CONSTRAINT pic_refs_date_order CHECK (end_date >= start_date);
|
|
113
|
+
|
|
114
|
+
-- migrate:down
|
|
115
|
+
ALTER TABLE pic_refs
|
|
116
|
+
DROP CONSTRAINT IF EXISTS pic_refs_date_order;
|
|
117
|
+
|
|
118
|
+
ALTER TABLE pic_refs
|
|
119
|
+
DROP CONSTRAINT IF EXISTS pic_refs_efficiency_positive;
|
|
120
|
+
|
|
121
|
+
ALTER TABLE pic_refs
|
|
122
|
+
ALTER COLUMN cost_per_kwh
|
|
123
|
+
SET
|
|
124
|
+
EXPRESSION AS (
|
|
125
|
+
CASE
|
|
126
|
+
WHEN start_range_km < end_range_km THEN NULL
|
|
127
|
+
ELSE round(
|
|
128
|
+
cost / NULLIF(
|
|
129
|
+
(start_range_km - end_range_km) * NULLIF(efficiency, 0),
|
|
130
|
+
0::NUMERIC
|
|
131
|
+
),
|
|
132
|
+
4
|
|
133
|
+
)
|
|
134
|
+
END
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
ALTER TABLE pic_refs
|
|
138
|
+
ALTER COLUMN kwh_per_real_1km
|
|
139
|
+
SET
|
|
140
|
+
EXPRESSION AS (
|
|
141
|
+
round(
|
|
142
|
+
(start_range_km - end_range_km) * NULLIF(efficiency, 0) / NULLIF(real_km, 0::NUMERIC),
|
|
143
|
+
6
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
ALTER TABLE pic_refs
|
|
148
|
+
ALTER COLUMN dropped_kwh
|
|
149
|
+
SET
|
|
150
|
+
EXPRESSION AS (
|
|
151
|
+
(start_range_km - end_range_km) * NULLIF(efficiency, 0)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
ALTER TABLE pic_refs
|
|
155
|
+
ALTER COLUMN mean_draw_kw
|
|
156
|
+
SET
|
|
157
|
+
EXPRESSION AS (
|
|
158
|
+
round(
|
|
159
|
+
(start_range_km - end_range_km) * NULLIF(efficiency, 0) / NULLIF(
|
|
160
|
+
EXTRACT(
|
|
161
|
+
epoch
|
|
162
|
+
FROM
|
|
163
|
+
(end_date - start_date)
|
|
164
|
+
) / 3600.0,
|
|
165
|
+
0::NUMERIC
|
|
166
|
+
),
|
|
167
|
+
3
|
|
168
|
+
)
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
ALTER TABLE pic_refs
|
|
172
|
+
ALTER COLUMN hours
|
|
173
|
+
SET
|
|
174
|
+
EXPRESSION AS (
|
|
175
|
+
round(
|
|
176
|
+
EXTRACT(
|
|
177
|
+
epoch
|
|
178
|
+
FROM
|
|
179
|
+
(end_date - start_date)
|
|
180
|
+
) / 3600.0,
|
|
181
|
+
2
|
|
182
|
+
)
|
|
183
|
+
);
|
package/db/schema.sql
CHANGED
|
@@ -415,17 +415,19 @@ CREATE TABLE public.pic_refs (
|
|
|
415
415
|
real_km numeric(8,4),
|
|
416
416
|
used_cp_ids integer[],
|
|
417
417
|
cost numeric(6,2),
|
|
418
|
-
hours numeric GENERATED ALWAYS AS (round((EXTRACT(epoch FROM (end_date - start_date)) / 3600.0),
|
|
419
|
-
mean_draw_kw numeric GENERATED ALWAYS AS (round((((start_range_km - end_range_km) *
|
|
418
|
+
hours numeric GENERATED ALWAYS AS (round((EXTRACT(epoch FROM (end_date - start_date)) / 3600.0), 6)),
|
|
419
|
+
mean_draw_kw numeric GENERATED ALWAYS AS (round((((start_range_km - end_range_km) * efficiency) / NULLIF((EXTRACT(epoch FROM (end_date - start_date)) / 3600.0), (0)::numeric)), 3)),
|
|
420
420
|
dropped_km numeric GENERATED ALWAYS AS ((start_range_km - end_range_km)),
|
|
421
|
-
dropped_kwh numeric GENERATED ALWAYS AS (((start_range_km - end_range_km) *
|
|
422
|
-
kwh_per_real_1km numeric GENERATED ALWAYS AS (round((((start_range_km - end_range_km) *
|
|
421
|
+
dropped_kwh numeric GENERATED ALWAYS AS (((start_range_km - end_range_km) * efficiency)),
|
|
422
|
+
kwh_per_real_1km numeric GENERATED ALWAYS AS (round((((start_range_km - end_range_km) * efficiency) / NULLIF(real_km, (0)::numeric)), 6)),
|
|
423
423
|
cost_per_real_100km numeric GENERATED ALWAYS AS (round(((cost * (100)::numeric) / NULLIF(real_km, (0)::numeric)), 2)),
|
|
424
424
|
cost_per_kwh numeric GENERATED ALWAYS AS (
|
|
425
425
|
CASE
|
|
426
426
|
WHEN (start_range_km < end_range_km) THEN NULL::numeric
|
|
427
|
-
ELSE round((cost / NULLIF(((start_range_km - end_range_km) *
|
|
428
|
-
END)
|
|
427
|
+
ELSE round((cost / NULLIF(((start_range_km - end_range_km) * efficiency), (0)::numeric)), 4)
|
|
428
|
+
END),
|
|
429
|
+
CONSTRAINT pic_refs_date_order CHECK ((end_date >= start_date)),
|
|
430
|
+
CONSTRAINT pic_refs_efficiency_positive CHECK ((efficiency > (0)::numeric))
|
|
429
431
|
);
|
|
430
432
|
|
|
431
433
|
|
|
@@ -2669,17 +2671,17 @@ CREATE VIEW public.pic_refs_v AS
|
|
|
2669
2671
|
ELSE r.cost
|
|
2670
2672
|
END AS dropped_cost,
|
|
2671
2673
|
CASE
|
|
2672
|
-
WHEN (r.ref_type = 'charge'::public.pic_ref_type) THEN (
|
|
2673
|
-
ELSE
|
|
2674
|
+
WHEN (r.ref_type = 'charge'::public.pic_ref_type) THEN (- s.gross_cost)
|
|
2675
|
+
ELSE s.gross_cost
|
|
2674
2676
|
END AS gross_dropped_cost,
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2677
|
+
s.gross_cost,
|
|
2678
|
+
s.gross_hours,
|
|
2679
|
+
g.gross_mean_draw_kw,
|
|
2680
|
+
s.gross_dropped_km,
|
|
2681
|
+
s.gross_dropped_kwh,
|
|
2682
|
+
g.gross_kwh_per_real_1km,
|
|
2683
|
+
g.gross_cost_per_real_100km,
|
|
2684
|
+
g.gross_cost_per_kwh,
|
|
2683
2685
|
sm.seed_address_id AS start_seed_address_id,
|
|
2684
2686
|
em.seed_address_id AS end_seed_address_id,
|
|
2685
2687
|
sm.cluster_id AS start_cluster_id,
|
|
@@ -2700,8 +2702,8 @@ CREATE VIEW public.pic_refs_v AS
|
|
|
2700
2702
|
END AS end_address_type,
|
|
2701
2703
|
COALESCE(sg.name, (sa.address)::character varying, (sa_fallback.address)::character varying) AS start_address,
|
|
2702
2704
|
COALESCE(eg.name, (ea.address)::character varying, (ea_fallback.address)::character varying) AS end_address
|
|
2703
|
-
FROM (((((((((public.pic_refs r
|
|
2704
|
-
LEFT JOIN public.pic_refs i ON (((i.ref_type = 'idle'::public.pic_ref_type) AND (r.ref_type = 'drive'::public.pic_ref_type) AND (i.next_type = r.ref_type) AND (i.next_id = r.ref_id))))
|
|
2705
|
+
FROM (((((((((((public.pic_refs r
|
|
2706
|
+
LEFT JOIN public.pic_refs i ON (((i.ref_type = 'idle'::public.pic_ref_type) AND (r.ref_type = 'drive'::public.pic_ref_type) AND (i.car_id = r.car_id) AND (i.next_type = r.ref_type) AND (i.next_id = r.ref_id))))
|
|
2705
2707
|
LEFT JOIN public.pic_cluster_map_v sm ON ((sm.address_id = r.start_address_id)))
|
|
2706
2708
|
LEFT JOIN public.pic_cluster_map_v em ON ((em.address_id = r.end_address_id)))
|
|
2707
2709
|
LEFT JOIN public.pic_addresses_v sa ON ((sa.id = sm.seed_address_id)))
|
|
@@ -2710,6 +2712,17 @@ CREATE VIEW public.pic_refs_v AS
|
|
|
2710
2712
|
LEFT JOIN public.pic_addresses_v ea_fallback ON ((ea_fallback.id = r.end_address_id)))
|
|
2711
2713
|
LEFT JOIN public.geofences sg ON ((sg.id = r.start_geofence_id)))
|
|
2712
2714
|
LEFT JOIN public.geofences eg ON ((eg.id = r.end_geofence_id)))
|
|
2715
|
+
CROSS JOIN LATERAL ( SELECT (r.cost + COALESCE(i.cost, 0.0)) AS gross_cost,
|
|
2716
|
+
(r.hours + COALESCE(i.hours, 0.0)) AS gross_hours,
|
|
2717
|
+
(r.dropped_km + COALESCE(i.dropped_km, 0.0)) AS gross_dropped_km,
|
|
2718
|
+
(r.dropped_kwh + COALESCE(i.dropped_kwh, 0.0)) AS gross_dropped_kwh) s)
|
|
2719
|
+
CROSS JOIN LATERAL ( SELECT round((s.gross_dropped_kwh / NULLIF(s.gross_hours, (0)::numeric)), 3) AS gross_mean_draw_kw,
|
|
2720
|
+
round((s.gross_dropped_kwh / NULLIF(r.real_km, (0)::numeric)), 6) AS gross_kwh_per_real_1km,
|
|
2721
|
+
round(((s.gross_cost * (100)::numeric) / NULLIF(r.real_km, (0)::numeric)), 2) AS gross_cost_per_real_100km,
|
|
2722
|
+
CASE
|
|
2723
|
+
WHEN (s.gross_dropped_kwh <= (0)::numeric) THEN NULL::numeric
|
|
2724
|
+
ELSE round((s.gross_cost / s.gross_dropped_kwh), 4)
|
|
2725
|
+
END AS gross_cost_per_kwh) g)
|
|
2713
2726
|
WHERE ((r.ref_id > 0) AND (COALESCE(r.prev_id, 0) >= 0));
|
|
2714
2727
|
|
|
2715
2728
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mxpicture/tesla-superchargers-mate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.63",
|
|
4
4
|
"description": "TeslaMate integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "MXPicture",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@mxpicture/tesla-superchargers-generated": "^0.1.
|
|
53
|
+
"@mxpicture/tesla-superchargers-generated": "^0.1.63",
|
|
54
54
|
"dbmate": "2.35.1",
|
|
55
55
|
"postgres": "^3.4.9",
|
|
56
56
|
"short-uuid": "^6.0.3",
|
package/sql/views.sql
CHANGED
|
@@ -1049,18 +1049,25 @@ SELECT
|
|
|
1049
1049
|
WHEN r.ref_type = 'charge'::pic_ref_type THEN - r.cost
|
|
1050
1050
|
ELSE r.cost
|
|
1051
1051
|
END AS dropped_cost,
|
|
1052
|
+
-- The gross_* block: this ref plus the idle that fed it. The extensive
|
|
1053
|
+
-- quantities -- cost, hours, dropped_km, dropped_kwh -- are summed in `s`
|
|
1054
|
+
-- and genuinely do add. The four ratios are recomputed over those sums in
|
|
1055
|
+
-- `g` and are never added: the sum of two rates is not the rate over the
|
|
1056
|
+
-- combined window. A 0.3 kW idle before a 25 kW drive reported 25.3 kW, and
|
|
1057
|
+
-- adding the two EUR/kWh figures was out by up to 0.5853 -- seven times the
|
|
1058
|
+
-- 0.08 EUR/kWh band that cost_per_kwh carries four decimals to resolve.
|
|
1052
1059
|
CASE
|
|
1053
|
-
WHEN r.ref_type = 'charge'::pic_ref_type THEN -
|
|
1054
|
-
ELSE
|
|
1060
|
+
WHEN r.ref_type = 'charge'::pic_ref_type THEN - s.gross_cost
|
|
1061
|
+
ELSE s.gross_cost
|
|
1055
1062
|
END AS gross_dropped_cost,
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1063
|
+
s.gross_cost,
|
|
1064
|
+
s.gross_hours,
|
|
1065
|
+
g.gross_mean_draw_kw,
|
|
1066
|
+
s.gross_dropped_km,
|
|
1067
|
+
s.gross_dropped_kwh,
|
|
1068
|
+
g.gross_kwh_per_real_1km,
|
|
1069
|
+
g.gross_cost_per_real_100km,
|
|
1070
|
+
g.gross_cost_per_kwh,
|
|
1064
1071
|
sm.seed_address_id AS start_seed_address_id,
|
|
1065
1072
|
em.seed_address_id AS end_seed_address_id,
|
|
1066
1073
|
sm.cluster_id AS start_cluster_id,
|
|
@@ -1083,8 +1090,11 @@ SELECT
|
|
|
1083
1090
|
COALESCE(eg.name, ea.address, ea_fallback.address) AS end_address
|
|
1084
1091
|
FROM
|
|
1085
1092
|
pic_refs r
|
|
1093
|
+
-- next_type/next_id identify the idle on their own, but the pack replay is
|
|
1094
|
+
-- per-car and the join says so.
|
|
1086
1095
|
LEFT JOIN pic_refs i ON i.ref_type = 'idle'::pic_ref_type
|
|
1087
1096
|
AND r.ref_type = 'drive'::pic_ref_type
|
|
1097
|
+
AND i.car_id = r.car_id
|
|
1088
1098
|
AND i.next_type = r.ref_type
|
|
1089
1099
|
AND i.next_id = r.ref_id
|
|
1090
1100
|
LEFT JOIN pic_cluster_map_v sm ON sm.address_id = r.start_address_id
|
|
@@ -1095,6 +1105,47 @@ FROM
|
|
|
1095
1105
|
LEFT JOIN pic_addresses_v ea_fallback ON ea_fallback.id = r.end_address_id
|
|
1096
1106
|
LEFT JOIN geofences sg ON sg.id = r.start_geofence_id
|
|
1097
1107
|
LEFT JOIN geofences eg ON eg.id = r.end_geofence_id
|
|
1108
|
+
-- Two chained LATERALs rather than generated columns on pic_refs: a
|
|
1109
|
+
-- generation expression cannot reference another generated column, and
|
|
1110
|
+
-- gross_mean_draw_kw needs both of `s`'s sums. Each returns exactly one row,
|
|
1111
|
+
-- so neither changes the cardinality above.
|
|
1112
|
+
CROSS JOIN LATERAL (
|
|
1113
|
+
SELECT
|
|
1114
|
+
r.cost + COALESCE(i.cost, 0.0) AS gross_cost,
|
|
1115
|
+
r.hours + COALESCE(i.hours, 0.0) AS gross_hours,
|
|
1116
|
+
r.dropped_km + COALESCE(i.dropped_km, 0.0) AS gross_dropped_km,
|
|
1117
|
+
r.dropped_kwh + COALESCE(i.dropped_kwh, 0.0) AS gross_dropped_kwh
|
|
1118
|
+
) s
|
|
1119
|
+
-- The ratios over those sums, each rounded to the scale of its per-row twin
|
|
1120
|
+
-- so that gross_X and X read at the same precision. gross_hours is what
|
|
1121
|
+
-- makes `hours` load-bearing rather than a display value, and why
|
|
1122
|
+
-- 20260907181456 took it to 6 dp.
|
|
1123
|
+
--
|
|
1124
|
+
-- The per-km pair divides by r.real_km alone: an idle covers no distance, so
|
|
1125
|
+
-- the drive's kilometres are the whole of the combined window's. That makes
|
|
1126
|
+
-- both a per-trip fact with a legitimately heavy tail -- 24 of 320 drives
|
|
1127
|
+
-- exceed 1 kWh/km, because a one-kilometre drive after a three-day park
|
|
1128
|
+
-- genuinely did cost that much per km once standby is charged to it. The
|
|
1129
|
+
-- car's own consumption rate is the adjacent kwh_per_real_1km on the same
|
|
1130
|
+
-- row; the tail is not a defect and nothing here thresholds it away.
|
|
1131
|
+
--
|
|
1132
|
+
-- What the tail does break is averaging. Aggregate these as
|
|
1133
|
+
-- sum(numerator) / sum(denominator), never avg(ratio): over the same drives,
|
|
1134
|
+
-- sum(gross_dropped_kwh) / sum(real_km) is 0.2212 kWh/km where
|
|
1135
|
+
-- avg(gross_kwh_per_real_1km) reads 0.4740. Both sums are columns of this
|
|
1136
|
+
-- view.
|
|
1137
|
+
CROSS JOIN LATERAL (
|
|
1138
|
+
SELECT
|
|
1139
|
+
round(s.gross_dropped_kwh / NULLIF(s.gross_hours, 0), 3) AS gross_mean_draw_kw,
|
|
1140
|
+
round(s.gross_dropped_kwh / NULLIF(r.real_km, 0), 6) AS gross_kwh_per_real_1km,
|
|
1141
|
+
round(s.gross_cost * 100 / NULLIF(r.real_km, 0), 2) AS gross_cost_per_real_100km,
|
|
1142
|
+
-- The same guard as pic_refs.cost_per_kwh, for the same reason: a
|
|
1143
|
+
-- non-positive kWh denominator yields a EUR/kWh that does not exist.
|
|
1144
|
+
CASE
|
|
1145
|
+
WHEN s.gross_dropped_kwh <= 0 THEN NULL
|
|
1146
|
+
ELSE round(s.gross_cost / s.gross_dropped_kwh, 4)
|
|
1147
|
+
END AS gross_cost_per_kwh
|
|
1148
|
+
) g
|
|
1098
1149
|
WHERE
|
|
1099
1150
|
-- The per-car seed (ref_id = -car_id) is a modelling artefact that gives the
|
|
1100
1151
|
-- pack its opening balance; it is not a charge that happened. Anything
|