# European Census Hub > The population and housing censuses of Europe, as published through the European Census Hub: every census hypercube Eurostat disseminates for the 2021, 2011, 2001 and 1991 rounds — all 143 cross-tabulations, 154,243,498 cells of them published here. The Census Hub catalogue puts the full cross-product of those same 143 cubes at 183,534,107 cells: every cube is present, but not every one is loaded to its catalogue size yet, and a `count(*)` over observations for a cube against that same cube's hypercubes.cell_count is the exact, always-current way to read off the gap rather than guess at it. The cubes cover the EU member states plus the EFTA and candidate countries. Population by age, sex, marital and family status, household and housing arrangements, country of birth and citizenship, educational attainment, activity status, occupation, industry, place of work, year of arrival and residence a year before the census, together with the housing tables — most of it down to NUTS 3 regions, which for Romania means all 41 counties and Bucharest. Every code is decoded from the source's own metadata: the nomenclature of each classification, the axes of each cube, and the NUTS geography with its level and country, so a cell can be read without a codebook. - Dataset id: `eu-census-hub` - Update cadence: monthly - Upstream source: https://ec.europa.eu/CensusHub/selectHyperCube?clearSession=true - Tags: census, eu, europe, eurostat, population, housing, demography, households, migration, nuts, regions, romania - Current version: ingest `01M1SPDT9JMYTQ7A589DTKSQC1`, snapshot 2026-09-05, generated 2026-09-05T21:28:25.528Z ## Licence and attribution - Licence: Creative Commons Attribution 4.0 International (`CC-BY-4.0`) - Licence text: https://ec.europa.eu/eurostat/web/main/help/copyright-notice - Required attribution: Source of data: Eurostat, European Census Hub (https://ec.europa.eu/CensusHub) — population and housing censuses of the EU member states, EFTA and candidate countries. - Notes: Eurostat authorises reuse of its statistical data and metadata for commercial and non-commercial purposes provided the source is acknowledged (Commission Decision 2011/833/EU; site content licensed CC BY 4.0). Two things a reuser must know about census figures in particular. First, the counts are produced by the NATIONAL statistical institutes — INS for Romania, Destatis for Germany, and so on — and transmitted to Eurostat under the census regulations (EC) 763/2008 and (EU) 2017/712; Eurostat disseminates them, and this dataset republishes them as retrieved, with Eurostat's own flags kept in the status column. Second, national institutes apply statistical disclosure control to census tables — rounding, perturbation and suppression of small counts — so a small figure is an approximation by design, a NULL with a "c" flag is a confidentiality suppression rather than a zero, and no individual can or should be identified from any combination of these cells. Eurostat's general permission does not extend to material it identifies as belonging to third parties, which this connector does not republish. Modified representations of the data must be identified as such to end users. Reproduce the attribution line verbatim anywhere you republish this data. ## Manifest - Start here for how this directory works: https://data.bunnytech.app/llms.txt - This dataset: https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/manifest.json - All datasets: https://data-directory.fsn1.your-objectstorage.com/public/index.json - Human page: https://data.bunnytech.app/datasets/eu-census-hub The manifest is the contract: file URLs, row counts, column types, min/max statistics and example queries all live there. Re-read it to discover a new version. ## Fastest path — Parquet exports Immutable, physically sorted snapshot exports. No catalog fetch, best row-group pruning, safe to cache forever. Register one view per table: ```sql INSTALL httpfs; LOAD httpfs; CREATE OR REPLACE VIEW observations AS SELECT * FROM read_parquet([ 'https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=1981/data_0.parquet', 'https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=1991/data_0.parquet', 'https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=2001/data_0.parquet', 'https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=2011/data_0.parquet', 'https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=2021/data_0.parquet' ]); CREATE OR REPLACE VIEW series AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/series.parquet'); CREATE OR REPLACE VIEW categories AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/categories.parquet'); CREATE OR REPLACE VIEW dimensions AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/dimensions.parquet'); CREATE OR REPLACE VIEW geographies AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/geographies.parquet'); CREATE OR REPLACE VIEW hypercubes AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/hypercubes.parquet'); ``` Example queries: -- Romania: population by marital status in every county, 2021 census: hypercube_code then country_code lead the sort order, so this predicate prunes to a few contiguous row groups. nuts_level = 3 is what stops the counties being added to the regions and the national total that sit in the same table. ```sql ATTACH IF NOT EXISTS 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/catalog.ducklake' AS "datadirectory" (READ_ONLY); SELECT g.geo_label AS county, s.series_label, o.value AS persons FROM datadirectory.eu_census_hub.observations o JOIN datadirectory.eu_census_hub.geographies g ON g.geo_code = o.geo JOIN datadirectory.eu_census_hub.series s ON s.hypercube_code = o.hypercube_code AND s.series_key = o.series_key WHERE o.hypercube_code = 'cens_21m_r3' AND o.country_code = 'RO' AND g.nuts_level = 3 AND s.dimension_codes ->> 'sex' = 'T' AND s.dimension_codes ->> 'age' = 'TOTAL' ORDER BY o.value DESC ``` -- Which census tables exist, and how big are they?: The catalogue covers every hypercube Eurostat publishes, whether or not observations for it have been loaded here — join to observations (see 'How complete is each cube's data?' below) before concluding a cube is empty. ```sql ATTACH IF NOT EXISTS 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/catalog.ducklake' AS "datadirectory" (READ_ONLY); SELECT hypercube_code, title, census_round, geo_level, cell_count FROM datadirectory.eu_census_hub.hypercubes WHERE title_lc LIKE '%educational attainment%' ORDER BY census_round DESC, cell_count DESC ``` ## Live lake — DuckLake catalog with time travel Attach the published catalog read-only over plain https. This works in native DuckDB AND in duckdb-wasm in the browser — the reader catalog resolves its data files through public https URLs, so no credentials and no backend are involved. ```sql INSTALL ducklake; LOAD ducklake; ATTACH 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/eu-census-hub/catalog.ducklake' AS eu_census_hub (READ_ONLY); SELECT * FROM eu_census_hub.main.observations LIMIT 5; USE eu_census_hub; ``` - Latest catalog: https://data-directory.fsn1.your-objectstorage.com/catalog/eu-census-hub/catalog.ducklake - This exact version: https://data-directory.fsn1.your-objectstorage.com/catalog/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/catalog.ducklake (sha256 fd7e85220883af103867112fe1ed3a9eea22b016af1e4b7f22aa2d75e2073753) - Written by DuckDB v1.5.5 - Time travel: this ingest is snapshot 38 — e.g. `SELECT * FROM eu_census_hub.observations AT (VERSION => 38)` ## Wholesale — S3 access For bulk downloads, read the bucket prefix directly over S3 with httpfs: ```sql INSTALL httpfs; LOAD httpfs; CREATE SECRET hetzner ( TYPE s3, ENDPOINT 'fsn1.your-objectstorage.com', URL_STYLE 'path', KEY_ID '', SECRET '' ); SELECT * FROM read_parquet('s3://data-directory/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/**/*.parquet'); ``` ## Schema ### observations The census itself: one row per hypercube, per statistical series, per geography — a count of people, households, families or dwellings from a European population and housing census. Every code in this table is decoded elsewhere: join series on (hypercube_code, series_key) for what is being counted, geographies on geo for where, and hypercubes on hypercube_code for the source metadata and licence. Always filter on hypercube_code first — the table holds four census rounds of unrelated cubes side by side, and their series keys are only unique within a hypercube. - 182.4M rows · 267 MB · write mode `upsert` - Physically sorted by: `hypercube_code`, `country_code`, `geo`, `series_key` - Primary key: `hypercube_code`, `census_year`, `geo`, `series_key` - Files: 5 - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=1981/data_0.parquet (759 rows) - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=1991/data_0.parquet (170.5K rows) - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=2001/data_0.parquet (5.5M rows) - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=2011/data_0.parquet (23M rows) - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/observations/census_year=2021/data_0.parquet (153.7M rows) Columns: - `hypercube_code` VARCHAR NOT NULL — Eurostat's code for the census hypercube, lowercase — e.g. "cens_21m_r3" (population by marital status, broad age groups and NUTS 3 region). The `_r2`/`_r3` suffix marks a regional cube. Joins to hypercubes.hypercube_code and series.hypercube_code. (range: "cens_01naagchi" … "cens_hnmga") (~147 distinct) - `census_year` INTEGER NOT NULL — Reference year of the census round the observation belongs to, taken from the cube's own time dimension: 2021, 2011, 2001 or 1991. The reference moment is the round's census day, which each country sets nationally (Romania's 2021 round counted the population as of 1 December 2021). (~5 distinct) - `country_code` VARCHAR NOT NULL — The country the geography belongs to: the first two letters of a NUTS code ("RO111" → "RO"), or the whole code for a supranational aggregate ("EU27_2020"). Denormalised from geo so that "all of Romania's regions" is a range on a sort column rather than a pattern match. (range: "AT" … "UK") - `geo` VARCHAR NOT NULL — The geography this cell counts: a NUTS code at country ("RO"), major-region ("RO1"), basic-region ("RO11") or small-region ("RO111") level, or the EU aggregate ("EU27_2020"). Joins to geographies.geo_code for the name and the level. Levels sit side by side and nest, so summing across geo double-counts — pick one level. (range: "AT" … "UKZZZ") (~2108 distinct) - `series_key` VARCHAR NOT NULL — Identifies what is being counted, inside this hypercube: the category codes of every dimension except geo and time, joined with "." in the cube's own dimension order — e.g. "A.WID_DTHREP.Y65-84.F.NR" is annual, widowed, aged 65–84, female, a headcount. Decode it by joining series, or read the individual codes from categories. (range: "A.0.NR" … "A.ZW.Y_LT5.T.PER") (~288497 distinct) - `value` DOUBLE — The census figure, in the unit named by series.unit_label — a count of persons in almost every cube, of dwellings or households in the housing ones. Counts are whole numbers. NULL where Eurostat publishes the cell with a flag but no figure, which for census data usually means confidentiality protection on a small population. - `status` VARCHAR — Eurostat observation flag on this cell: "c" confidential, "e" estimated, "p" provisional, "d" definition differs, "u" low reliability, "b" break in series; flags can combine. NULL means a definitive, unflagged figure — which most census cells are. Census counts are commonly rounded or perturbed by the national institute for disclosure control, so small figures should be read as approximate even unflagged. ### series One row per statistical series in a hypercube: what a cell of observations actually counts, with every classification code decoded into the English label Eurostat publishes for it. Geography-independent — the same series is counted in every region of the cube. Upserted, so labels follow the source's current wording while first/last_seen_date record when the series was present. - 440.7K rows · 5.4 MB · write mode `upsert` - Physically sorted by: `hypercube_code`, `series_key` - Primary key: `hypercube_code`, `series_key` - Text search columns: `series_label` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/series.parquet (440.7K rows) Columns: - `hypercube_code` VARCHAR NOT NULL — The hypercube the series belongs to; joins to observations.hypercube_code and hypercubes.hypercube_code. Series keys are only unique within a hypercube. (range: "cens_01naagchi" … "cens_hnmga") (~147 distinct) - `series_key` VARCHAR NOT NULL — The series identifier exactly as it appears on observations.series_key: non-geo, non-time category codes joined with "." in the cube's dimension order. (range: "A.0.NR" … "A.ZW.Y_LT5.T.PER") (~288497 distinct) - `series_label` VARCHAR NOT NULL — What the series counts, in English: the labels of its classification categories joined with " · " in dimension order — e.g. "Annual · Widowed persons and persons whose registered partnership ended with the death of the partner · From 65 to 84 years · Females · Number". What a chart legend or a table header should show. - `series_label_lc` VARCHAR NOT NULL — series_label lowercased and accent-folded, for case- and accent-insensitive search. Query this, not lower(series_label) — a function on a column cannot use parquet statistics. - `unit_code` VARCHAR — Category code of the cube's unit dimension for this series — "NR" (a plain count) in nearly every census cube. NULL when the cube declares no unit dimension. - `unit_label` VARCHAR — English label of unit_code, e.g. "Number". - `dimension_codes` JSON NOT NULL — The full coordinate of the series as {dimension id: category code}, excluding geo and time — e.g. {"freq":"A","marsta":"WID_DTHREP","age":"Y65-84","sex":"F","unit":"NR"}. The machine-readable form of series_key; the dimension ids are described in the dimensions table. - `dimension_labels` JSON NOT NULL — English label for each coordinate, keyed by dimension id — the same keys as dimension_codes, so a row can be rendered without any further joins. - `first_seen_date` DATE NOT NULL — The first run day (UTC) on which this series appeared in the hypercube. Maintained automatically by the upsert loader — never overwritten, only widened. - `last_seen_date` DATE NOT NULL — The most recent run day (UTC) on which the hypercube still carried this series. ### categories The census nomenclature: one row per category of every dimension of every loaded hypercube, with the code, its English label and its position on the axis. This is the table that answers "what age bands does this cube use", "what does UNK mean here" and "which cubes cover NUTS 3 regions of Romania" — Eurostat reuses codelists across cubes but each cube carries only the subset it needs, so coverage is per hypercube and not global. Upserted. - 79.5K rows · 118 KB · write mode `upsert` - Physically sorted by: `hypercube_code`, `dimension_id`, `position` - Primary key: `hypercube_code`, `dimension_id`, `category_code` - Text search columns: `category_label` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/categories.parquet (79.5K rows) Columns: - `hypercube_code` VARCHAR NOT NULL — The hypercube whose dimension carries this category. (range: "cens_01naagchi" … "cens_hnmga") (~147 distinct) - `dimension_id` VARCHAR NOT NULL — The dimension the category belongs to, as the source names it — "marsta", "age", "sex", "geo", "unit", "freq". Joins to dimensions.dimension_id. (range: "TIME_PERIOD" … "y_const") (~35 distinct) - `category_code` VARCHAR NOT NULL — The category code as published, e.g. "WID_DTHREP", "Y65-84", "F", "RO111". This is the literal string that appears inside observations.series_key and observations.geo. (~3270 distinct) - `category_label` VARCHAR — Eurostat's English label for the code — the census definition in words. NULL only when the source published the code with no label. - `category_label_lc` VARCHAR — category_label lowercased and accent-folded, for case- and accent-insensitive search. Query this, not lower(category_label). - `position` INTEGER NOT NULL — Zero-based position of the category along its axis, in the order the source lists it. Census classifications are published in a meaningful order (age bands ascending, "Total" first) that alphabetising the codes would destroy — sort on this to present them the way the statistical office does. (range: 0 … 1884) - `first_seen_date` DATE NOT NULL — The first run day (UTC) on which this category appeared on this dimension. Maintained automatically by the upsert loader. - `last_seen_date` DATE NOT NULL — The most recent run day (UTC) on which the dimension still carried this category. ### dimensions The axes of each loaded hypercube, in the source's own order: what a cube is broken down by, what each breakdown is called in English, and how many categories it has. Read this before querying a cube you have not seen — the dimension order here is the order the codes appear in observations.series_key. Upserted. - 989 rows · 6.1 KB · write mode `upsert` - Physically sorted by: `hypercube_code`, `position` - Primary key: `hypercube_code`, `dimension_id` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/dimensions.parquet (989 rows) Columns: - `hypercube_code` VARCHAR NOT NULL — The hypercube this axis belongs to. (range: "cens_01naagchi" … "cens_hnmga") (~147 distinct) - `dimension_id` VARCHAR NOT NULL — The dimension id as the source names it: "geo", "time", "marsta", "age", "sex", "unit", "freq". Joins to categories.dimension_id and keys series.dimension_codes. (~35 distinct) - `dimension_label` VARCHAR — Eurostat's English name for the axis, e.g. "Age class", "Marital status", "Geopolitical entity (reporting)". NULL when the source published none. - `position` INTEGER NOT NULL — Zero-based position of the dimension in the cube, which is also its position in observations.series_key once geo and time are removed. (range: 0 … 7) - `role` VARCHAR NOT NULL — What the axis is for: "classification" (a census breakdown — the interesting ones), "geo" (the geography), "time" (the census round), "unit" (the unit of measure) or "frequency" (Eurostat plumbing, always annual for a census). - `category_count` INTEGER [categories] — Number of categories this cube carries on the axis. - `in_series_key` BOOLEAN NOT NULL — True when this dimension contributes a code to observations.series_key — every axis except geo and time. - `first_seen_date` DATE NOT NULL — The first run day (UTC) on which the hypercube carried this dimension. Maintained automatically by the upsert loader. - `last_seen_date` DATE NOT NULL — The most recent run day (UTC) on which the hypercube still carried this dimension. ### geographies The geography codelist behind observations.geo: every NUTS region and country the loaded census cubes report, with its English name, its country and its NUTS level. The four levels nest — RO contains RO1 contains RO11 contains RO111 — so a query that does not pin nuts_level will count the same people up to four times. Upserted. - 2,602 rows · 41 KB · write mode `upsert` - Physically sorted by: `country_code`, `nuts_level`, `geo_code` - Primary key: `geo_code` - Text search columns: `geo_label` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/geographies.parquet (2,602 rows) Columns: - `geo_code` VARCHAR NOT NULL — The code as published, matching observations.geo: "RO", "RO1", "RO11", "RO111", "EU27_2020". (range: "AT" … "UKZZZ") (~2108 distinct) - `geo_label` VARCHAR NOT NULL — English name of the region as Eurostat publishes it: "Romania", "Bihor", "Bistriţa-Năsăud", "European Union - 27 countries (from 2020)". - `geo_label_lc` VARCHAR NOT NULL — geo_label lowercased and accent-folded, for case- and accent-insensitive search — the only way to find "Bistrita-Nasaud" typed without diacritics. Query this, not lower(geo_label). - `country_code` VARCHAR NOT NULL — The country this geography belongs to, matching observations.country_code: the first two letters of a NUTS code, or the whole code for a supranational aggregate. (range: "AT" … "UK") - `nuts_level` INTEGER — NUTS level: 0 country, 1 major socio-economic region, 2 basic region for regional policy, 3 small region. NULL for a supranational aggregate such as EU27_2020. Pin this in any query that sums across regions. (range: 0 … 3) - `is_aggregate` BOOLEAN NOT NULL — True for a supranational aggregate (EU27_2020) rather than a reporting country or one of its regions. Exclude it before ranking countries against each other. - `first_seen_date` DATE NOT NULL — The first run day (UTC) on which this geography appeared in any loaded cube. Maintained automatically by the upsert loader. - `last_seen_date` DATE NOT NULL — The most recent run day (UTC) on which a loaded cube still carried this geography. ### hypercubes The catalogue: one row for EVERY census hypercube Eurostat disseminates through the European Census Hub — all four rounds, whether or not this run loaded its observations — with the title and theme path the source publishes, the census year, how deep its geography goes, its full cell count, and the exact API URL that returns it. Identity only: whether a cube is actually loaded, and how much of it, is answered exactly by a GROUP BY over observations against this table's cell_count, never by a stored flag — see the migrations comment below for why. Upserted, so a newly published cube arrives with a fresh first_seen_date. - 143 rows · 12 KB · write mode `upsert` - Physically sorted by: `census_round`, `hypercube_code` - Primary key: `hypercube_code` - Text search columns: `title` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/eu-census-hub/v/01M1SPDT9JMYTQ7A589DTKSQC1/hypercubes.parquet (143 rows) Columns: - `hypercube_code` VARCHAR NOT NULL — Eurostat's code for the hypercube, lowercase — the key that observations, series, categories and dimensions all join on. (range: "cens_01naagchi" … "cens_hnmga") (~147 distinct) - `title` VARCHAR NOT NULL — Eurostat's English title of the hypercube, e.g. "Population by marital status, broad age groups and NUTS 3 region". It names the cube's dimensions in order and is the first thing to read when choosing one. - `title_lc` VARCHAR NOT NULL — title lowercased and accent-folded, for case- and accent-insensitive search. Query this, not lower(title). - `census_round` INTEGER — The census round the cube belongs to, from Eurostat's own code naming (cens_21… → 2021): 2021, 2011, 2001 or 1991. NULL for the few cross-round summary tables whose code carries no round. For the year the DATA is referenced to, use census_year. (range: 1991 … 2021) - `census_year` INTEGER — Reference year of the data: the cube's own time dimension when this run loaded it, else the catalogue's own reference year for the cube — the same value either way, so a narrower later run never regresses it. NULL only for a cube the catalogue gives no year for either. - `theme_path` VARCHAR — The cube's folder path in Eurostat's navigation tree, joined with " > " — e.g. "Population and housing censuses > Census 2021 round > Population by family and household characteristics". The source's own subject grouping. - `geo_level` VARCHAR — How deep the cube's geography goes, from its own structure document: "countries", "NUTS 1 regions", "NUTS 2 regions" or "NUTS 3 regions". This is the cube's full, unsliced structure — read the same way regardless of which country a run happened to be scoped to — so NULL only when that structure could not be fetched at all. Eurostat also encodes this in the code suffix (_r2, _r3). - `geo_count` INTEGER [geographies] — Number of geographies the cube contains, per its published structure — before any country slicing any run applied. Structure-derived like geo_level, so stable across the country fan-out; NULL only when the structure could not be fetched at all. - `cell_count` BIGINT [cells] — Total cells in the cube's full cross-product, as the catalogue reports it. The four rounds together are about 184 million cells, and the largest single cube is 34.4 million — the number to check before asking for one whole. - `data_updated_at` DATE — Last update of the cube's data, per the catalogue's own "dd.mm.yyyy" column — the source names no time-of-day or zone, so the connector's tocDate() parser (census.ts) fixes one: it is stored as the UTC day, built with Date.UTC() rather than the host clock. Census figures are revised after publication, which is why observations are merged rather than appended. - `structure_changed_at` DATE — Last change to the cube's table structure, per the catalogue — the same "dd.mm.yyyy" column parsed the same way as data_updated_at: stored as the UTC day via tocDate() (census.ts), since the catalogue itself states no time-of-day or zone. - `api_url` VARCHAR — The Statistics API request that returns this cube whole as JSON-stat. Mind cell_count first: the largest census cubes answer with hundreds of megabytes. - `browser_url` VARCHAR — Eurostat's data browser page for the cube — the human view of the same numbers. - `census_hub_url` VARCHAR — The European Census Hub query builder, the browser front end these hypercubes are published through. The same for every row; kept so a reader can always get back to the source application. - `first_seen_date` DATE NOT NULL — The first run day (UTC) on which the catalogue carried this cube — how a newly published census table is spotted. Maintained automatically by the upsert loader. - `last_seen_date` DATE NOT NULL — The most recent run day (UTC) on which the catalogue still carried this cube. ## Performance notes - There are no indexes in a lakehouse. The physical sort order plus Parquet row-group min/max statistics ARE the index. Predicates on the leading sort columns prune whole row groups; everything else is a full column scan. - `observations` prunes on `hypercube_code` → `country_code` → `geo` → `series_key`. - `series` prunes on `hypercube_code` → `series_key`. - `categories` prunes on `hypercube_code` → `dimension_id` → `position`. - `dimensions` prunes on `hypercube_code` → `position`. - `geographies` prunes on `country_code` → `nuts_level` → `geo_code`. - `hypercubes` prunes on `census_round` → `hypercube_code`. - `ILIKE '%needle%'` cannot prune: it decompresses that whole column. Prefer prefix predicates on the lowercased search companion columns where they exist. - Every file URL under `/v//` is immutable. Cache it as aggressively as you like. - Files are never rewritten in place — a new ingest writes a new version path. Re-read the manifest to pick up updates rather than polling the file URLs. - Pull only the columns you need. Parquet is columnar and the reader range-GETs what it reads. ## Do not - Do not scrape https://data.bunnytech.app HTML. Every number on those pages comes from the manifest above. - Do not hit the upstream source (https://ec.europa.eu/CensusHub/selectHyperCube?clearSession=true) in bulk. This mirror exists so you do not have to. - Do not read DuckLake internal data files with a plain Parquet reader. They come with positional delete files, and ignoring those silently returns deleted rows. Use the ducklake extension, or use the published exports above. - Do not republish without the attribution line, and do not imply endorsement.