Data & Infrastructure

BigQuery in Schools: A Complete Guide to Implementation, Operations, and Potential

Systemic Analysis
BigQuery in Schools: A Complete Guide to Implementation, Operations, and Potential

From data silos to actionable insight — for IT admins, school leaders, and educator-innovators


Why Schools Need a Data Warehouse

Most schools have been collecting data for years — attendance, grades, survey results, security logs, activity across learning platforms. The problem isn’t a lack of data. The problem is that it sits in separate, sealed containers: one system here, another there, a paper register somewhere else, an air quality sensor in a third browser tab. A teacher who wants to understand whether a student has problems not just with maths but also with attendance on Wednesday lessons and engagement on Classroom — has to click through three systems manually.

BigQuery is the answer to that problem. It’s Google Cloud Platform’s fully managed, serverless data warehouse, capable of analysing billions of rows in seconds. For a state school, that sounds like using a cannon to swat a fly — but the detail matters. BigQuery performs well even with thousands of rows, is free up to 10 GB of stored data and 1 TB of queries per month, integrates natively with Google Workspace, and doesn’t require you to be a developer to operate it.

This article is not for people who want to know what the cloud is. It’s for those who manage a school’s digital infrastructure and want concrete answers: how to configure it, how to connect data from Classroom, what to do with security logs, and how to present everything to the head teacher in a readable dashboard.

Who this article is for: For the Workspace admin who wants to know how to set it up. For the head teacher who wants to understand what they’re getting. For the educator-innovator who wants to build their own analyses. Each section starts with the basics and goes deeper — read as far as you need.


Architecture: How Data Gets into BigQuery

Before tackling configuration, it’s worth understanding the data flow. In a typical Google Workspace school environment, there are usually several sources:

  • Google Classroom API — student activity, assignments, deadlines, grades, comments
  • Google Workspace Admin SDK — security logs, sign-ins, Workspace actions
  • Google Forms / Sheets — surveys, club registrations, event sign-ups
  • External systems — electronic registers (Librus, Vulcan), IoT sensors (Airly), library systems

Two Deployment Models

In practice, schools use one of two approaches to feed BigQuery:

Model 1: Apps Script as the ETL Layer

The simplest and most accessible approach for schools using Google Workspace. Apps Script pulls data from the Classroom API or other Google sources and writes it to BigQuery via the built-in BigQueryApp service.

// Example: exporting assignment results to BigQuery
function exportAssignmentsToBQ() {
  const projectId = "your-gcp-project";
  const datasetId = "classroom_data";
  const tableId   = "assignments";

  const courses = Classroom.Courses.list().courses || [];
  const rows = [];

  courses.forEach(course => {
    const works = Classroom.Courses.CourseWork.list(course.id).courseWork || [];
    works.forEach(work => {
      const subs = Classroom.Courses.CourseWork
        .StudentSubmissions.list(course.id, work.id).studentSubmissions || [];
      subs.forEach(sub => {
        rows.push({ insertId: sub.id, json: {
          course_id:    course.id,
          course_name:  course.name,
          work_id:      work.id,
          work_title:   work.title,
          student_id:   sub.userId,   // pseudonymise in production!
          state:        sub.state,
          late:         sub.late || false,
          grade:        sub.assignedGrade || null,
          timestamp:    new Date().toISOString()
        }});
      });
    });
  });

  if (rows.length === 0) return;

  BigQuery.Tabledata.insertAll(
    { rows }, projectId, datasetId, tableId
  );
}

GDPR note: In a production environment, student identifiers must be pseudonymised — instead of an email address or name, use a UUID generated once and stored in a separate mapping table. This way an analyst sees patterns but cannot access personal data without the appropriate permissions.

Model 2: Direct Integration via Google Cloud

A more advanced variant, where data flows into BigQuery through several channels simultaneously: Pub/Sub for real-time events, Cloud Functions or Cloud Run for processing data from external APIs, and Data Transfer Service for ready-made connectors (e.g. Google Analytics, YouTube Analytics).

# Pipeline schema for Airly sensor data (Python / Cloud Function)

def airly_to_bq(request):
    import requests, google.cloud.bigquery as bq
    from datetime import datetime

    API_KEY      = "your_airly_key"
    INSTALLATION = "12345"
    PROJECT      = "your-gcp-project"
    DATASET      = "environmental"
    TABLE        = "airly_readings"

    r = requests.get(
        f"https://airapi.airly.eu/v2/measurements/installation"
        f"?installationId={INSTALLATION}",
        headers={"apikey": API_KEY}
    ).json()

    current = r["current"]["values"]
    row = {
        "timestamp":   datetime.utcnow().isoformat(),
        "pm25":        next((v["value"] for v in current if v["name"]=="PM2.5"), None),
        "pm10":        next((v["value"] for v in current if v["name"]=="PM10"),  None),
        "temperature": next((v["value"] for v in current if v["name"]=="TEMPERATURE"), None),
    }

    client = bq.Client(project=PROJECT)
    table  = client.get_table(f"{PROJECT}.{DATASET}.{TABLE}")
    client.insert_rows_json(table, [row])
    return "ok"

Configuration Step by Step

1. Google Cloud Platform Project

BigQuery lives within a GCP project. If you use Google Workspace for Education, your school already has access to GCP within the same Google organisation. I recommend creating a dedicated project — e.g. school-analytics-2026 — separate from other school resources. This keeps billing, permissions, and audit trails clean.

  1. Go to console.cloud.google.com
  2. Create a new project: Resource Manager → Create Project
  3. Select the school organisation (important for permissions!)
  4. Enable BigQuery API: APIs & Services → Enable APIs → search “BigQuery”
  5. Enable Classroom API and Admin SDK API if using Apps Script

Data location: For schools in the EU, choose europe-central2 (Warsaw) or europe-west1 (Belgium). Student data processed under GDPR should not leave the EU. Location is set once, when creating a dataset — it cannot be changed afterwards.

2. Database Schema — Datasets and Tables

A dataset is equivalent to a schema in a traditional database — a container for tables. I recommend separate datasets for different domains:

DatasetContents
classroom_dataGoogle Classroom activity: courses, assignments, submissions, grades
workspace_auditSecurity logs: sign-ins, Drive access, admin actions
environmentalSensor data: Airly, temperature, humidity
journal_dataImport from electronic register: attendance, grades, notes
analyticsAggregated views and tables: foundation for dashboards

Tables in BigQuery have a schema — explicitly defined columns with types. Here’s an example schema for the student activity table:

// JSON schema for student_activity table
[
  { "name": "event_id",     "type": "STRING",    "mode": "REQUIRED" },
  { "name": "course_id",    "type": "STRING",    "mode": "REQUIRED" },
  { "name": "course_name",  "type": "STRING",    "mode": "NULLABLE" },
  { "name": "student_uuid", "type": "STRING",    "mode": "REQUIRED" },
  { "name": "event_type",   "type": "STRING",    "mode": "REQUIRED" },
  { "name": "work_id",      "type": "STRING",    "mode": "NULLABLE" },
  { "name": "work_title",   "type": "STRING",    "mode": "NULLABLE" },
  { "name": "state",        "type": "STRING",    "mode": "NULLABLE" },
  { "name": "late",         "type": "BOOLEAN",   "mode": "NULLABLE" },
  { "name": "grade",        "type": "FLOAT64",   "mode": "NULLABLE" },
  { "name": "max_grade",    "type": "FLOAT64",   "mode": "NULLABLE" },
  { "name": "timestamp",    "type": "TIMESTAMP", "mode": "REQUIRED" },
  { "name": "school_year",  "type": "STRING",    "mode": "NULLABLE" }
]

3. Permissions and Security

Permission management in BigQuery is based on IAM (Identity and Access Management) roles. In a school environment, apply the principle of least privilege:

RoleWhen to assign
roles/bigquery.dataViewerTeacher viewing their own data — read only
roles/bigquery.dataEditorApps Script writing data — read and write to tables
roles/bigquery.jobUserTechnical account running queries (e.g. Looker Studio)
roles/bigquery.adminIT admin — full control. Assign with care
roles/bigquery.dataOwnerDataset owner — can grant permissions within their scope

The service account for Apps Script or Cloud Functions should never have admin permissions. Grant it roles/bigquery.dataEditor on a specific dataset, not the entire project.


SQL in School Practice

BigQuery uses Google Standard SQL — compatible with ANSI SQL. If you’ve ever written queries in MySQL, PostgreSQL, or even Access, you’ll pick it up in a few hours. Below are examples that are directly useful in schools.

Query 1: Students at Risk of Failing

Find students who submitted fewer than 50% of assignments in the last 30 days, have at least 3 late submissions, and whose weighted average has dropped below passing:

SELECT
  student_uuid,
  COUNT(*) AS total_works,
  COUNTIF(state = "TURNED_IN" OR state = "RETURNED") AS submitted,
  ROUND(COUNTIF(state = "TURNED_IN" OR state = "RETURNED")
        / COUNT(*) * 100, 1) AS submission_rate_pct,
  COUNTIF(late = TRUE) AS late_count,
  ROUND(AVG(grade / NULLIF(max_grade, 0)) * 100, 1) AS weighted_avg_pct
FROM
  `project.classroom_data.student_activity`
WHERE
  timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
  AND max_grade IS NOT NULL
GROUP BY
  student_uuid
HAVING
  submission_rate_pct < 50
  OR late_count >= 3
  OR weighted_avg_pct < 50
ORDER BY
  submission_rate_pct ASC
LIMIT 50;

Query 2: Correlation Between Attendance and Results

Do students with more than 15% absence have statistically lower grades? The query joins register data with Classroom data:

WITH attendance AS (
  SELECT
    student_uuid,
    ROUND(COUNTIF(status = "ABSENT") / COUNT(*) * 100, 1) AS absence_pct
  FROM `project.journal_data.attendance`
  WHERE school_year = "2025/2026"
  GROUP BY student_uuid
),
grades AS (
  SELECT
    student_uuid,
    ROUND(AVG(grade / NULLIF(max_grade, 0)) * 100, 1) AS avg_score_pct
  FROM `project.classroom_data.student_activity`
  WHERE grade IS NOT NULL AND max_grade > 0
  GROUP BY student_uuid
)
SELECT
  a.student_uuid,
  a.absence_pct,
  g.avg_score_pct,
  CASE
    WHEN a.absence_pct < 5  THEN "low (<5%)"
    WHEN a.absence_pct < 15 THEN "moderate (5–15%)"
    ELSE "high (>15%)"
  END AS absence_group
FROM attendance a
JOIN grades g USING (student_uuid)
ORDER BY a.absence_pct DESC;

Query 3: Security Analysis — Suspicious Sign-ins

From the workspace_audit dataset, pull accounts that signed in from more than 3 different countries within a week — a classic signal of a compromised account:

SELECT
  actor_email,
  COUNT(DISTINCT country_code) AS unique_countries,
  ARRAY_AGG(DISTINCT country_code) AS countries,
  COUNT(*) AS total_logins,
  MIN(event_time) AS first_login,
  MAX(event_time) AS last_login
FROM
  `project.workspace_audit.login_events`
WHERE
  event_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
  AND event_type = "login"
  AND result = "SUCCESS"
GROUP BY
  actor_email
HAVING
  unique_countries >= 3
ORDER BY
  unique_countries DESC, total_logins DESC;

Tip: Run this query daily via Cloud Scheduler and send results by email to the admin via Apps Script. You don’t need to sit watching a dashboard — let the data come to you.

Query 4: Air Quality vs. Student Activity

On days with high PM2.5, do students submit fewer assignments? The query joins Airly sensor data with Classroom data:

WITH daily_air AS (
  SELECT
    DATE(timestamp) AS day,
    ROUND(AVG(pm25), 1) AS avg_pm25,
    CASE
      WHEN AVG(pm25) < 25  THEN "good"
      WHEN AVG(pm25) < 50  THEN "moderate"
      ELSE "poor"
    END AS air_quality
  FROM `project.environmental.airly_readings`
  WHERE timestamp >= "2025-09-01"
  GROUP BY day
),
daily_activity AS (
  SELECT
    DATE(timestamp) AS day,
    COUNT(*) AS submissions
  FROM `project.classroom_data.student_activity`
  WHERE state = "TURNED_IN"
  GROUP BY day
)
SELECT
  a.day,
  a.avg_pm25,
  a.air_quality,
  COALESCE(s.submissions, 0) AS submissions
FROM daily_air a
LEFT JOIN daily_activity s USING (day)
ORDER BY a.day;

Looker Studio: From Data to Decisions

BigQuery without a visualisation layer is an engine without a steering wheel. Looker Studio (formerly Data Studio) is Google’s free tool for building interactive dashboards — and its native integration with BigQuery is one of the biggest advantages of this stack.

Connecting BigQuery to Looker Studio

  1. In Looker Studio, create a new data source
  2. Select the BigQuery connector
  3. Sign in with an account that has the roles/bigquery.jobUser role
  4. Select the project, dataset, and table (or view)
  5. Configure data refresh (cache from 1h to 12h)

I recommend using Views in BigQuery as sources for Looker Studio — not tables directly. A view is a saved SQL query that: hides raw data, applies pseudonymisation, filters out unnecessary columns, and lets you change logic without modifying the report.

-- Example: a teacher-safe view
CREATE OR REPLACE VIEW `project.analytics.teacher_dashboard_v` AS
SELECT
  course_name,
  work_title,
  state,
  late,
  ROUND(grade / NULLIF(max_grade, 0) * 100, 0) AS score_pct,
  FORMAT_TIMESTAMP("%Y-%m-%d", timestamp) AS date
FROM `project.classroom_data.student_activity`
-- student_uuid intentionally omitted — teacher sees trends, not individuals
WHERE school_year = "2025/2026";
DashboardContents
Teacher panelCourse activity, submission rate, grade distribution, weekly trend
Form tutor panelClass absence, at-risk students, comparison with previous year
Head teacher panelSchool-level KPIs, annual trends, benchmarks between classes
IT admin panelSecurity logs, suspicious accounts, data sync status
Environmental panelAir quality, correlations with attendance, seasonal trends

Row-Level Security trick: Looker Studio doesn’t have built-in RLS, but you can simulate it through BigQuery. Create a view that filters data based on the SESSION_USER() function — the email of the signed-in user. Each teacher sees only their own courses, while the head teacher sees everything.


Case Studies: BigQuery in Action

Case 1: Leonardo Piwoni School, Szczecin

As part of the school’s digitisation project, a pipeline was built: Google Classroom API → Apps Script (ETL) → BigQuery → Looker Studio. A nightly trigger (23:00) pulls activity from all courses and writes it to BigQuery. Total execution time: approx. 4 minutes for 750 students and 60 teachers.

The key insight after the first year: data reveals patterns invisible in day-to-day contact. Classes that appeared active had a high rate of late submissions clustered just before the deadline — a signal of last-minute learning. Quieter-seeming classes had a more even distribution of work throughout the week.

Measurable result: The time a form tutor needed to identify at-risk students dropped from approx. 45 minutes (clicking through systems) to 2 minutes (opening the dashboard). Pedagogical intervention became reactive to live data, not retrospective.

Case 2: Workspace Security Monitoring

The school’s IT admin exports logs from the Google Workspace Admin SDK to BigQuery using the automatic export (BigQuery Export in Admin Console settings — available from Workspace Business Starter). Logs flow automatically into BigQuery without writing any code.

From this data, you can build alerts for: sign-ins from new devices outside school, bulk file downloads from Drive, permission changes by any account other than the admin, and sign-in attempts to services disabled for the school.

Case 3: Integration with the Electronic Register

Librus and Vulcan don’t have official APIs for schools on standard plans. The most effective approach: daily CSV export from the register (a feature available in both systems), upload to Google Drive by the teacher or office, automatic processing by Apps Script into BigQuery. The entire pipeline is automated via a Drive trigger — every new CSV file in a designated folder triggers the import function.

// Drive folder trigger — automatic CSV import
function setupDriveTrigger() {
  const folderId = "YOUR_EXPORTS_FOLDER_ID";
  ScriptApp.newTrigger("onNewCsvInDrive")
    .forSpreadsheet(SpreadsheetApp.create("trigger-helper"))
    .onChange()
    .create();
}

function onNewCsvInDrive() {
  const folder = DriveApp.getFolderById("YOUR_EXPORTS_FOLDER_ID");
  const files  = folder.getFilesByType("text/csv");
  while (files.hasNext()) {
    const file = files.next();
    if (!file.getName().startsWith("processed_")) {
      importCsvToBigQuery(file);
      file.setName("processed_" + file.getName());
    }
  }
}

Case 4: Airly Air Quality Sensor

The school’s “Green Shield” project combines air quality monitoring with educational data. An Airly sensor measures PM2.5, PM10, and temperature hourly. A Cloud Function (free tier: 2M calls/month) pulls data from the Airly API and writes it to BigQuery.

After six months of data collection, the school has material for science, physics, and statistics lessons — and Looker Studio shows children in real time what they’re breathing. This isn’t decoration: students came forward with a proposal to produce regular reports for the Parents’ Council.


Costs and the Free Tier

One of the most frequent questions is “how much does this cost?” The honest answer: for a typical school — almost nothing or nothing at all.

ComponentFree tier
Data storageFirst 10 GB per month free. One year of school data (750 students) is typically 2–5 GB.
QueriesFirst 1 TB of data processed per month free. A typical school dashboard uses approx. 1–10 GB/month.
Streaming insertThe only non-zero cost for small schools: $0.01 per 200 MB. With 750 students, approx. $1–3/month. Can be avoided by using batch mode.
BigQuery MLFirst 10 GB of ML queries per month free.
Looker StudioFree, no limits.

How to avoid surprises: Set a budget alert in GCP Console (Billing → Budgets & Alerts). A threshold of $5/month with an email alert gives complete peace of mind. In a year of running a school pipeline, bills typically range between $0 and $2/month.

BigQuery vs. Alternatives for Schools

SolutionAssessment for schools
Google Sheets5M cell limit, no SQL, no scalability. Good for reports, not for analysis.
Looker Studio without BQLimited transformation capabilities. No complex queries.
PostgreSQL (VPS)Full control, but requires server administration, backups, security — serious ongoing effort.
BigQueryServerless, free within school scale, native Workspace integration, SQL, ML, scalability.

Implementation Plan: From Zero to Working System

A realistic timeline for one person (IT admin + determination) without external support:

PhaseTasks
Week 1GCP project, datasets, first tables. Trial export from one Classroom course to BQ. Schema verification.
Week 2Full Apps Script pipeline. UUID pseudonymisation. First view in Looker Studio.
Week 3Nightly trigger. Testing on full year’s data. Identifying and fixing edge cases (missing grades, deleted courses).
Week 4Teacher dashboard. Pilot with 2–3 willing form tutors. Feedback collection.
Month 2Head teacher panel. Integration with register or sensor. Documentation. Training for a backup admin.
Month 3+Predictive analytics (BigQuery ML). Automated alerts. Expansion to additional data sources.

Common Pitfalls

  • Schema mismatch: Apps Script returns null where BigQuery expects STRING. Always validate types before insertAll.
  • Duplicates from triggers: If a trigger fires twice, you get double entries. Use insertId as a deduplicator — BigQuery automatically ignores duplicates for approx. 1 minute.
  • getActiveSpreadsheet() in a trigger: In a time-based trigger, this function returns null. Always use openById() with an explicit spreadsheet ID.
  • No error handling: If a course is archived or deleted, the Classroom API throws an exception. Wrap loops in try/catch and log errors to a separate table in BQ.
  • Query costs in Looker Studio: By default, Looker Studio queries BQ on every page refresh. Enable cache (minimum 1h) — for school data, refreshing every 4–12h is entirely sufficient.

BigQuery ML: Predictive Analytics Without Python

BigQuery ML lets you train machine learning models directly in SQL, without exporting data to external tools. For schools, the most interesting applications are: predicting risk of failure (classification) and predicting absence (regression).

Risk Prediction Model

-- Creating a classification model (logistic regression)
CREATE OR REPLACE MODEL `project.analytics.risk_model`
OPTIONS(
  model_type = "logistic_reg",
  input_label_cols = ["at_risk"],
  l2_reg = 0.1
) AS
SELECT
  submission_rate,
  late_rate,
  avg_score,
  absence_pct,
  IF(avg_score < 0.5 OR absence_pct > 20, TRUE, FALSE) AS at_risk
FROM `project.analytics.student_features_v`;

-- Prediction for the current period
SELECT
  student_uuid,
  predicted_at_risk,
  predicted_at_risk_probs[OFFSET(1)].prob AS risk_probability
FROM ML.PREDICT(
  MODEL `project.analytics.risk_model`,
  (SELECT * FROM `project.analytics.student_features_current_v`)
)
ORDER BY risk_probability DESC
LIMIT 20;

Important perspective: Predictive models in schools are support tools, not verdicts. A model result should reach the form tutor as “worth having a conversation with this student” — not as a label in a system. A DPIA (Data Protection Impact Assessment) is required under GDPR for any automated profiling of students.


Summary

BigQuery is not a solution looking for a problem. It’s infrastructure that lets a school ask questions about its own data — and get answers faster than a weekly reporting cycle.

Three things worth remembering:

  1. Start small. One dataset, one Apps Script, one dashboard for one form tutor. Success at small scale is the best argument for the head teacher.
  2. Data requires trust. The technical pipeline is 30% of the work. 70% is agreeing with teachers what they want to see, how to interpret data, and where the tool ends and the conversation with the student begins.
  3. GDPR is not a threat. Pseudonymisation, DPIA, processing records — that’s two hours of documentation work that brings years of peace of mind and shows the school operates seriously.

The full pipeline code (Apps Script + BQ schema + Looker Studio views) is available in the workspace.edu.pl repository on GitHub. Questions and experiences from your own deployments are welcome in the comments or at GEG Poland.