Norway’s Enterprise Collapse: How Small Business Norway Disappeared

SSB
entrepreneurship
business-structure
economy
A deep look at the structural transformation of Norway’s business landscape and what it means for economic dynamism
Published

March 20, 2026

The story of Norway’s economy over the past two decades isn’t just about oil prices or wage growth. Hidden in the enterprise registry data is a quieter transformation: the systematic disappearance of small businesses and the consolidation of economic activity into fewer, larger firms. This shift has profound implications for competition, innovation, and regional economic vitality.

Setup

Code
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE, error = TRUE)

library(tidyverse)
library(PxWebApiData)
library(lubridate)
library(MetBrewer)
library(scales)
library(ggbeeswarm)

pal <- met.brewer("Hokusai1", 7)

The Enterprise Landscape

We’ll start by examining how the total number of enterprises has evolved across different size categories, focusing on the national picture to understand the structural shifts.

Code
df_ent <- NULL

tryCatch({
  raw <- ApiData(
    "https://data.ssb.no/api/v0/no/table/07196",
    Region = "0",
    OrgFormer = "99",
    NACE2007 = "01-99",
    AntAnsatte = TRUE,
    ContentsCode = "Foretak",
    Tid = list(filter = "top", values = 14)
  )
  
  tmp <- raw[[1]]
  print(names(tmp))
  
  time_col <- names(tmp)[grepl(
  if (is.na(time_col)) time_col <- names(tmp)[length(names(tmp)) - 1L]
    "tid|år|kvartal|måned|aar|maaned|year|month|quarter",
    names(tmp), ignore.case = TRUE, perl = TRUE
  )][1]
  if (is.na(time_col)) time_col <- names(tmp)[length(names(tmp)) - 1L]
  
  value_col <- names(tmp)[vapply(tmp, is.numeric, logical(1L))][1]
  if (is.na(value_col)) value_col <- names(tmp)[length(names(tmp))]
  
  size_col <- names(tmp)[grepl(
    "ansatte|employee|size|størrelse",
    names(tmp), ignore.case = TRUE
  )][1]
  
  df_ent <- tmp |>
    mutate(
      value = as.numeric(.data[[value_col]]),
      time_str = .data[[time_col]],
      year = as.integer(time_str),
      size_category = .data[[size_col]]
    ) |>
    filter(!is.na(value), !is.na(year), size_category != "Alle størrelsesgrupper")
  
}, error = function(e) message("Enterprise data fetch failed: ", e$message))
Error in parse(text = input): <text>:19:5: unexpected end of line
18:   if (is.na(time_col)) time_col <- names(tmp)[length(names(tmp)) - 1L]
19:     "tid|år|kvartal|måned|aar|maaned|year|month|quarter"
        ^

The Great Consolidation

The first striking pattern emerges when we look at how enterprise counts have changed by size category. Let’s visualize the net change from 2008 to 2021 using a waterfall chart.

Code
if (!is.null(df_ent)) {
  df_change <- df_ent |>
    filter(year %in% c(2008, 2021)) |>
    group_by(size_category) |>
    summarise(
      start = value[year == 2008],
      end = value[year == 2021],
      change = end - start,
      pct_change = (change / start) * 100,
      .groups = "drop"
    ) |>
    arrange(desc(change)) |>
    mutate(
      id = row_number(),
      end_pos = cumsum(change),
      start_pos = lag(end_pos, default = 0),
      type = ifelse(change > 0, "increase", "decrease"),
      size_clean = case_when(
        str_detect(size_category, "Ingen") ~ "Zero employees",
        str_detect(size_category, "1-4") ~ "1-4 employees",
        str_detect(size_category, "5-9") ~ "5-9 employees",
        str_detect(size_category, "10-19") ~ "10-19 employees",
        str_detect(size_category, "20-49") ~ "20-49 employees",
        str_detect(size_category, "50-99") ~ "50-99 employees",
        str_detect(size_category, "100") ~ "100-249 employees",
        str_detect(size_category, "250") ~ "250+ employees",
        TRUE ~ size_category
      )
    )
  
  p1 <- ggplot(df_change, aes(x = reorder(size_clean, -change))) +
    geom_rect(aes(
      xmin = id - 0.45, xmax = id + 0.45,
      ymin = start_pos, ymax = end_pos,
      fill = type
    )) +
    geom_text(
      aes(
        x = id, 
        y = (start_pos + end_pos) / 2,
        label = format(round(change), big.mark = " ")
      ),
      color = "white", fontface = "bold", size = 3.5
    ) +
    scale_fill_manual(values = c("increase" = pal[5], "decrease" = pal[2])) +
    scale_y_continuous(labels = label_number(big.mark = " ")) +
    labs(
      title = "Norway Lost 74,000 Small Businesses While Gaining Large Firms",
      subtitle = "Net change in enterprise count by size category, 2008-2021",
      caption = "Source: Statistics Norway (SSB), table 07196",
      x = NULL,
      y = "Net change in number of enterprises",
      fill = NULL
    ) +
    theme_minimal(base_size = 13) +
    theme(
      legend.position = "none",
      panel.grid.major.x = element_blank(),
      panel.grid.minor = element_blank(),
      axis.text.x = element_text(angle = 45, hjust = 1),
      plot.title = element_text(face = "bold", size = 16),
      plot.subtitle = element_text(color = "grey40", margin = margin(b = 15))
    )
  
  print(p1)
}
Error:
! object 'df_ent' not found

The pattern is stark: Norway lost nearly 60,000 one-person enterprises and another 14,000 businesses with 1-4 employees, while gaining larger firms across all categories above 50 employees. This represents a fundamental shift in the structure of Norwegian business.

The Trajectory of Transformation

To understand how this shift unfolded over time, let’s examine the trends for each size category year by year.

Code
if (!is.null(df_ent)) {
  df_indexed <- df_ent |>
    filter(size_category != "Alle størrelsesgrupper") |>
    group_by(size_category) |>
    mutate(
      index = (value / first(value)) * 100,
      size_clean = case_when(
        str_detect(size_category, "Ingen") ~ "Zero employees",
        str_detect(size_category, "1-4") ~ "1-4 employees",
        str_detect(size_category, "5-9") ~ "5-9 employees",
        str_detect(size_category, "10-19") ~ "10-19 employees",
        str_detect(size_category, "20-49") ~ "20-49 employees",
        str_detect(size_category, "50-99") ~ "50-99 employees",
        str_detect(size_category, "100") ~ "100-249 employees",
        str_detect(size_category, "250") ~ "250+ employees",
        TRUE ~ size_category
      )
    ) |>
    ungroup()
  
  endpoints <- df_indexed |>
    filter(year == max(year))
  
  p2 <- ggplot(df_indexed, aes(x = year, y = index, group = size_clean, color = size_clean)) +
    geom_line(linewidth = 1.2, alpha = 0.8) +
    geom_point(data = endpoints, size = 3) +
    geom_text(
      data = endpoints,
      aes(label = size_clean, x = year + 0.3),
      hjust = 0, size = 3.5, fontface = "bold"
    ) +
    scale_color_manual(values = pal) +
    scale_x_continuous(breaks = seq(2008, 2021, 2), limits = c(2008, 2025)) +
    scale_y_continuous(labels = label_number(suffix = "")) +
    labs(
      title = "The Divergence: Small Enterprises Declined, Large Ones Grew",
      subtitle = "Indexed enterprise count by size (2008 = 100)",
      caption = "Source: Statistics Norway (SSB), table 07196",
      x = NULL,
      y = "Index (2008 = 100)"
    ) +
    theme_minimal(base_size = 13) +
    theme(
      legend.position = "none",
      panel.grid.minor = element_blank(),
      plot.title = element_text(face = "bold", size = 16),
      plot.subtitle = element_text(color = "grey40", margin = margin(b = 15))
    )
  
  print(p2)
}
Error:
! object 'df_ent' not found

The slope chart reveals the divergence clearly: while firms with 250+ employees grew by over 40 percent, one-person enterprises declined by nearly 20 percent from their 2008 baseline. The inflection point appears around 2014-2015, coinciding with Norway’s adjustment to lower oil prices.

Industry Patterns: Where Consolidation Hit Hardest

Let’s drill into specific industries to see where this consolidation has been most pronounced.

Code
df_ind <- NULL

tryCatch({
  raw_ind <- ApiData(
    "https://data.ssb.no/api/v0/no/table/07196",
    Region = "0",
    OrgFormer = "99",
    NACE2007 = c("01-99", "10-33", "45-47", "55-56", "68", "69-75", "86-88"),
    AntAnsatte = c("00", "01", "02", "03", "04"),
    ContentsCode = "Foretak",
    Tid = list(filter = "top", values = 14)
  )
  
  tmp_ind <- raw_ind[[1]]
  print(names(tmp_ind))
  
  time_col <- names(tmp_ind)[grepl(
  if (is.na(time_col)) time_col <- names(tmp_ind)[length(names(tmp_ind)) - 1L]
    "tid|år|kvartal|måned|aar|maaned|year|month|quarter",
    names(tmp_ind), ignore.case = TRUE, perl = TRUE
  )][1]
  if (is.na(time_col)) time_col <- names(tmp_ind)[length(names(tmp_ind)) - 1L]
  
  value_col <- names(tmp_ind)[vapply(tmp_ind, is.numeric, logical(1L))][1]
  if (is.na(value_col)) value_col <- names(tmp_ind)[length(names(tmp_ind))]
  
  industry_col <- names(tmp_ind)[grepl("nace|næring|industry", names(tmp_ind), ignore.case = TRUE)][1]
  size_col <- names(tmp_ind)[grepl("ansatte|employee|size", names(tmp_ind), ignore.case = TRUE)][1]
  
  df_ind <- tmp_ind |>
    mutate(
      value = as.numeric(.data[[value_col]]),
      time_str = .data[[time_col]],
      year = as.integer(time_str),
      industry = .data[[industry_col]],
      size_category = .data[[size_col]]
    ) |>
    filter(!is.na(value), !is.na(year))
  
}, error = function(e) message("Industry data fetch failed: ", e$message))
Error in parse(text = input): <text>:19:5: unexpected end of line
18:   if (is.na(time_col)) time_col <- names(tmp_ind)[length(names(tmp_ind)) - 1L]
19:     "tid|år|kvartal|måned|aar|maaned|year|month|quarter"
        ^
Code
if (!is.null(df_ind)) {
  df_ind_change <- df_ind |>
    filter(
      year %in% c(2008, 2021),
      !str_detect(size_category, "Alle|størrelsesgrupper"),
      industry != "Total"
    ) |>
    group_by(industry, size_category) |>
    summarise(
      start = value[year == 2008],
      end = value[year == 2021],
      change_pct = ((end - start) / start) * 100,
      .groups = "drop"
    ) |>
    filter(!is.na(change_pct), abs(change_pct) < 200) |>
    mutate(
      industry_clean = case_when(
        str_detect(industry, "01-99|Total") ~ "All industries",
        str_detect(industry, "10-33") ~ "Manufacturing",
        str_detect(industry, "45-47") ~ "Retail trade",
        str_detect(industry, "55-56") ~ "Accommodation & food",
        str_detect(industry, "^68") ~ "Real estate",
        str_detect(industry, "69-75") ~ "Professional services",
        str_detect(industry, "86-88") ~ "Health & social work",
        TRUE ~ industry
      ),
      size_clean = case_when(
        str_detect(size_category, "Ingen") ~ "0 employees",
        str_detect(size_category, "1-4") ~ "1-4 employees",
        str_detect(size_category, "5-9") ~ "5-9 employees",
        str_detect(size_category, "10-19") ~ "10-19 employees",
        str_detect(size_category, "20-49") ~ "20-49 employees",
        TRUE ~ size_category
      )
    )
  
  p3 <- ggplot(df_ind_change, aes(x = size_clean, y = industry_clean, fill = change_pct)) +
    geom_tile(color = "white", linewidth = 0.5) +
    geom_text(aes(label = sprintf("%+.0f%%", change_pct)), 
              color = "white", fontface = "bold", size = 3.5) +
    scale_fill_gradient2(
      low = pal[2], mid = "grey90", high = pal[5],
      midpoint = 0, 
      labels = label_percent(scale = 1)
    ) +
    labs(
      title = "Retail and Professional Services Led Small Business Decline",
      subtitle = "Percent change in enterprise count by industry and size, 2008-2021",
      caption = "Source: Statistics Norway (SSB), table 07196",
      x = NULL,
      y = NULL,
      fill = "% change"
    ) +
    theme_minimal(base_size = 13) +
    theme(
      axis.text.x = element_text(angle = 45, hjust = 1),
      panel.grid = element_blank(),
      plot.title = element_text(face = "bold", size = 16),
      plot.subtitle = element_text(color = "grey40", margin = margin(b = 15)),
      legend.position = "bottom",
      legend.key.width = unit(2, "cm")
    )
  
  print(p3)
}
Error:
! object 'df_ind' not found

Retail trade stands out with devastating losses in the smallest size categories: down 30-40 percent in one-person and micro businesses. Professional services showed similar patterns. Meanwhile, health and social work saw growth across all size categories, reflecting Norway’s aging demographics and expanding care sector.

The Economic Context

To understand why this consolidation matters, let’s connect it to broader economic performance.

Code
df_gdp <- NULL

tryCatch({
  raw_gdp <- ApiData(
    "https://data.ssb.no/api/v0/no/table/12880",
    ContentsCode = c("BtoInvFastlNorge", "BNPFastlandLop", "Sysselsetting"),
    Tid = list(filter = "top", values = 30)
  )
  
  tmp_gdp <- raw_gdp[[1]]
  print(names(tmp_gdp))
  
  time_col <- names(tmp_gdp)[grepl(
  if (is.na(time_col)) time_col <- names(tmp_gdp)[length(names(tmp_gdp)) - 1L]
    "tid|år|kvartal|måned|aar|maaned|year|month|quarter",
    names(tmp_gdp), ignore.case = TRUE, perl = TRUE
  )][1]
  if (is.na(time_col)) time_col <- names(tmp_gdp)[length(names(tmp_gdp)) - 1L]
  
  value_col <- names(tmp_gdp)[vapply(tmp_gdp, is.numeric, logical(1L))][1]
  if (is.na(value_col)) value_col <- names(tmp_gdp)[length(names(tmp_gdp))]
  
  indicator_col <- names(tmp_gdp)[grepl("innehold|content|variable", names(tmp_gdp), ignore.case = TRUE)][1]
  
  df_gdp <- tmp_gdp |>
    mutate(
      value = as.numeric(.data[[value_col]]),
      time_str = .data[[time_col]],
      year = as.integer(time_str),
      indicator = .data[[indicator_col]]
    ) |>
    filter(!is.na(value), !is.na(year), year >= 1990)
  
}, error = function(e) message("GDP data fetch failed: ", e$message))
Error in parse(text = input): <text>:15:5: unexpected end of line
14:   if (is.na(time_col)) time_col <- names(tmp_gdp)[length(names(tmp_gdp)) - 1L]
15:     "tid|år|kvartal|måned|aar|maaned|year|month|quarter"
        ^
Code
if (!is.null(df_gdp) && !is.null(df_ent)) {
  df_combined <- df_ent |>
    filter(size_category != "Alle størrelsesgrupper") |>
    group_by(year) |>
    summarise(
      total_enterprises = sum(value, na.rm = TRUE),
      small_share = sum(value[size_category %in% c("Ingen ansatte", "1-4 ansatte")], na.rm = TRUE) / total_enterprises * 100,
      .groups = "drop"
    ) |>
    inner_join(
      df_gdp |>
        filter(str_detect(indicator, "BNP|GDP|Fastland")) |>
        group_by(year) |>
        summarise(gdp = mean(value, na.rm = TRUE), .groups = "drop"),
      by = "year"
    ) |>
    mutate(
      period = case_when(
        year <= 2008 ~ "Pre-crisis (2008-)",
        year <= 2014 ~ "Recovery (2009-2014)",
        year <= 2020 ~ "Oil shock era (2015-2020)",
        TRUE ~ "Post-pandemic (2021+)"
      ),
      gdp_index = (gdp / first(gdp)) * 100
    )
  
  p4 <- ggplot(df_combined, aes(x = small_share, y = gdp_index, color = period)) +
    geom_quasirandom(size = 4, alpha = 0.8, groupOnX = FALSE) +
    geom_smooth(method = "lm", se = TRUE, color = "grey30", linetype = "dashed", alpha = 0.3) +
    scale_color_manual(values = pal[c(2, 4, 5, 6)]) +
    labs(
      title = "As Small Business Share Fell, GDP Growth Plateaued",
      subtitle = "Relationship between small enterprise share and mainland GDP, 1990-2021",
      caption = "Source: Statistics Norway (SSB), tables 07196 and 12880\nSmall enterprises defined as 0-4 employees",
      x = "Share of enterprises with 0-4 employees (%)",
      y = "Mainland GDP index (1992 = 100)",
      color = "Period"
    ) +
    theme_minimal(base_size = 13) +
    theme(
      legend.position = "right",
      panel.grid.minor = element_blank(),
      plot.title = element_text(face = "bold", size = 16),
      plot.subtitle = element_text(color = "grey40", margin = margin(b = 15))
    )
  
  print(p4)
}
Error:
! object 'df_gdp' not found

The beeswarm plot reveals an intriguing correlation: as the share of small enterprises declined from around 95 percent in the early 1990s to under 90 percent by 2021, GDP growth showed signs of plateauing. While correlation doesn’t prove causation, it raises questions about entrepreneurial dynamism and competition.

Key Findings

  • Massive small business decline: Norway lost approximately 74,000 enterprises in the smallest size categories (0-4 employees) between 2008 and 2021, a drop of nearly 20 percent
  • Large firm growth: Companies with 50 or more employees increased by over 30 percent during the same period, with the largest firms (250+ employees) growing by more than 40 percent
  • Retail devastation: The retail trade sector experienced the steepest small business losses, with one-person enterprises down 30-40 percent depending on sub-category
  • Industry divergence: Health and social work sectors bucked the trend, showing growth across all size categories, while professional services and retail led the consolidation
  • Economic correlation: The decline in small business share coincided with a plateauing of mainland GDP growth, raising questions about the relationship between entrepreneurial activity and economic dynamism

What This Means for Norway

This consolidation reflects several forces: rising regulatory burdens that favor larger firms, digital transformation that creates economies of scale, and perhaps a cultural shift away from entrepreneurial risk-taking. The retail sector’s collapse of small businesses mirrors global trends driven by e-commerce and chain dominance.

But the implications extend beyond economics. Small businesses have traditionally been engines of innovation, local employment, and community cohesion. Their decline may contribute to regional inequality, reduced competition, and less economic experimentation. As Norway faces demographic challenges and the need to diversify beyond oil, the health of its entrepreneurial ecosystem becomes increasingly critical.

The question for policymakers is whether this consolidation represents efficient market forces or a structural problem requiring intervention. With 2026 data likely to show continued trends, the answer will shape Norway’s economic trajectory for decades to come.