Automation guide
Google Sheets as a database and the signs you've outgrown it
Google Sheets is a perfectly good database for a small team with simple data, and most businesses outgrow it in a predictable order: editing rules first, then lookups between tabs, broken automations, slow formulas and finally permissions. Once three or more of those signs show up, move the data to Postgres and keep the sheet as a read-only view while you switch over one piece at a time.
By Max Winter-Leinweber7 min read
What does Google Sheets do well as a database, and when does it stop?
Google Sheets works as a database while a few people edit simple, flat data and nothing outside the sheet depends on its layout. It stops working when your data needs rules, reliable links between tables, sturdy automations or per-person access, because a spreadsheet enforces none of those.
If your business runs on a sheet, that was probably the right call when you started. It costs nothing beyond your Google Workspace plan, your team already knows it, and you can add a column without waiting for a developer.
A sheet rarely fails in one go. As you grow, more people edit it, more tabs refer to each other and more tools read from it. The problems arrive in a fairly predictable order, and knowing where you are in it tells you how long you have before something breaks in front of a customer.
The warning signs, in the order they usually appear
The early signs are about people, the middle ones about automations, the late ones about access and safety.
1. A row of red cells saying do not touch
More people start editing. Someone sorts one column on its own and scrambles a week of entries, so a coloured row appears listing the columns nobody may change. Meanwhile the sheet accepts a mistyped date, a duplicate email or a blank required field without complaint.
2. More lookups than data
Your customers get their own tab with an ID, and other tabs pull their details across with XLOOKUP. Nothing enforces the link: delete a customer and the rows that referred to them point at nothing, change an ID and every lookup quietly returns blank. This is the sheet trying to be a relational database.
3. An automation breaks because a column moved
Someone inserts a column mid-sheet, and any script that reads column D by position now reads the wrong field. The error only shows in an execution log nobody checks. Once Zapier, Make or your website also read the sheet, every column rename is a small outage.
4. Clean-up becomes a weekly job
The same customer spelled three ways, dates in four formats, phone numbers that lost their leading zero. Someone spends an afternoon a week fixing it.
5. The file gets slow
Lookups, IMPORTRANGE and array formulas recalculate across the file. A formula-heavy sheet often gets painful at a few tens of thousands of rows, nowhere near the official limit of 10 million cells per spreadsheet, which is shared across every tab and counts empty cells inside the grid.
6. Quota errors and timeouts
The Sheets API limits requests per minute: at the time of writing, 300 reads and 300 writes per project, and 60 of each per user. Go over and you get 429 errors until the minute resets. An Apps Script run is capped at six minutes, so long jobs need chunking. Adding delays and retries means you're working around the tool.
7. A customer wants to see only their own records
This is usually the hard stop. Sharing is per file, so anyone who can open the spreadsheet sees every tab and row; protected ranges stop edits, not reading. Separating customers means a file per customer, kept in sync by hand.
8. Nobody can say who has access
The sheet holds names, addresses and phone numbers, it's still shared with people who left and with personal Gmail accounts, and restoring an earlier version brings back the whole file rather than one overwritten row. There's no independent backup. With personal data, that's more than an inconvenience.
What to move to
For most businesses at this point, the answer is Postgres behind a small app. It enforces links with foreign keys, rejects bad values with constraints, joins tables in one query instead of thousands of formulas, and shows each person only their rows.
Postgres and an app. The right home when customers or staff log in, when automations can't afford to break, or when the data holds anything personal. Managed providers include automated backups and point in time recovery.
A CRM. If your sheet is really a customer list with notes and follow-ups, an off-the-shelf CRM may solve it without a build.
Airtable. A reasonable halfway house for linked records and field types, with its own record limits and per-seat pricing, and the same ownership questions later on.
An outgrown spreadsheet also makes a surprisingly good spec: it shows exactly how your business already works.
Move gradually, and keep the sheet as a view
Don't switch everything in one weekend. Put the database underneath, keep your team looking at a sheet they recognise, and move the inputs across one at a time, so each tab only ever has one place where it's edited.
Map what exists. Every tab, column, formula, form, script and automation, and who relies on each. Hidden columns and helper tabs are where the undocumented rules live.
Design and clean. One table per kind of thing, with real types and constraints for every rule the sheet only hoped people would follow. Then clean: duplicates, dates, phone numbers kept as text, and cells holding several values split into rows. This step takes longest, because someone who knows the business has to decide each case.
Import on repeat. Script the import from the Sheets API or a CSV export, keep the original row number in a legacy column, and rerun against fresh copies until the counts match. At this stage the sheet is still the master.
Flip one input at a time. Replace a Google Form or manual entry with a form in the app, so new entries land in the database. From then on, a scheduled job writes that data back to its tab, so your team still sees it in the sheet. Repeat for each input, and move Apps Script logic into server jobs you can monitor and retry.
Lock the sheet. Once nothing writes to the sheet directly, make it view-only. It stays as a reporting view for the people who like to filter and chart in a spreadsheet, fed one way from the database.
For the sync, we'd use a scheduled job that runs a query hourly or overnight and overwrites a tab through the Sheets API, batching requests to spare the quota. Apps Script's JDBC service can connect Sheets to Postgres directly, but your database then has to accept connections from Google's address ranges, which we wouldn't do for customer data. Either way, use a read-only database user and send only the columns the report needs.
Frequently asked questions
- Is Google Sheets a good client database?
- For a few hundred clients and a couple of people managing them, it's fine. It struggles once each client needs to see only their own records, or you need to link clients to jobs and invoices reliably. At that point a CRM or a proper database is the safer place for the data.
- Can I use Google Forms as a database?
- Not on its own. Google Forms collects responses and writes each one as a row in a linked Google Sheet, so the sheet is the database. That's good for intake, but the form can't look up, edit or check against existing records.
- How many rows can Google Sheets handle?
- The limit is on cells, not rows: a spreadsheet holds up to 10 million cells, shared across all tabs, and Google has raised that figure before, so check the current one. A sheet full of lookup formulas usually slows down well before the limit, often at a few tens of thousands of rows.
- How do I connect Google Sheets to PostgreSQL or MySQL?
- The safest option is a scheduled job that runs a query and writes the results into the sheet through the Sheets API. Apps Script's JDBC service and connector add-ons also work, though add-ons need database credentials. Use a read-only database user and treat the sheet as a report, not a place to edit data.