Load
Read the three sheets (consumption, rates, building master), lowercased the column names and loaded them into an energy schema.
Three Excel sheets became a relational PostgreSQL database. I unpivoted the data, joined consumption to yearly prices, and worked out where the energy and the money go.
Read the three sheets (consumption, rates, building master), lowercased the column names and loaded them into an energy schema.
Converted date to a real date type and renamed columns to snake_case so they can be queried without quotes.
Composite primary keys on consumption (date, building) and rates (year, energy_type), a primary key on building, and a foreign key from consumption to building.
Why: prevents duplicate months and orphan buildings
Water, electricity and gas sat in three columns. A UNION ALL view turned them into one type / consumption pair so every question needs one query, not three.
Why: one long table is far easier to group and join
Joined consumption to rates on both year and energy type, because prices change every year.
Why: joining on type alone would price every year at the same rate
Water 186.2M units · electricity 21.7M · gas 2.55M. Units differ per type, so compare cost, not volume.
B1006 uses the most (20.1M). B1009 uses the least (17.8M).
New York leads on both consumption (57.3M) and cost (4.35M), but it has 3 buildings. Houston is lowest on cost (1.44M) with just 1 building.
Water 10.85M · gas 3.01M · electricity 2.02M. Water is the most expensive every year, rising from 2.26M to 3.06M.
B1008 at 1.50M. Eight of eleven buildings cost more than the average.
monthly_energy_summary and dataset_calendar are ready for a BI tool.
alter table energy.consumption add constraint key1 primary key (date, building); alter table energy.rate add constraint key2 primary key (year, energy_type); alter table energy.building add constraint key3 primary key (building); alter table energy.consumption add constraint fk foreign key (building) references energy.building (building);
create view energy.unpivoted as
with unpivoted as (
select date, building, 'water' as type, water_consumption as consumption from energy.consumption
union all
select date, building, 'electricity' as type, electricity_consumption as consumption from energy.consumption
union all
select date, building, 'gas' as type, gas_consumption as consumption from energy.consumption
)
select * from unpivoted;-- cost = consumption x the price for that energy type AND that year
select type,
round(sum(consumption * price_per_unit)::numeric, 2) as total_cost
from energy.unpivoted
join energy.rate
on energy.unpivoted.type = energy.rate.energy_type
and extract(year from energy.unpivoted.date) = energy.rate.year
group by type
order by total_cost desc;-- buildings costing more than the average building
with total_cost_per_building as (
select building,
round(sum(consumption * price_per_unit)::numeric, 2) as total_cost
from energy.unpivoted
join energy.rate
on energy.rate.energy_type = energy.unpivoted.type
and extract(year from energy.unpivoted.date) = energy.rate.year
group by building
)
select building, total_cost
from total_cost_per_building
where total_cost > (select avg(total_cost) from total_cost_per_building)
order by total_cost desc;-- most expensive energy type in each year (window function)
with yearly_ranked as (
select year, type, total_cost,
rank() over (partition by year order by total_cost desc) as rank_in_year
from (
select year, type,
round(sum(consumption * price_per_unit)::numeric, 2) as total_cost
from energy.rate
join energy.unpivoted
on energy.rate.energy_type = energy.unpivoted.type
and extract(year from energy.unpivoted.date) = energy.rate.year
group by year, type
) as yearly_costs
)
select year, type, total_cost, rank_in_year
from yearly_ranked
where rank_in_year = 1
order by year;create view energy.dataset_calendar as
with dataset_calendar as (
select generate_series(
(select min(date) from energy.unpivoted),
(select max(date) from energy.unpivoted),
interval '1 day')::date as date
)
select date,
extract(year from date) as year,
extract(month from date) as month,
extract(day from date) as day,
trim(to_char(date, 'Month')) as month_name,
trim(to_char(date, 'Day')) as day_name
from dataset_calendar;