Blank rows are usually harmless until they aren't. SUMIFS skips them fine. But VLOOKUP without IFERROR blows up on them, pivot tables refuse to auto-expand through them, and any formula counting rows with COUNTA or referencing 'GL Detail'!A:A will silently miscount. In a 3,200-row GL dump with 280 separator rows baked in by an export script, that's the kind of error that surfaces in a board pack at the worst possible time.
Method 1: FILTER Formula to Delete Blank Rows in Google Sheets (Non-Destructive)
This doesn't delete anything - it outputs a clean version of your range while leaving the source intact. That distinction matters when the source sheet is locked, shared, or fed by an import.
=FILTER('GL Detail'!A2:F3200, 'GL Detail'!A2:A3200 <> "")
Drop this in a staging tab. Your formulas downstream point at the staging tab instead of the raw import. When the import refreshes with new blanks, the FILTER output updates automatically. No manual intervention.
For a multi-column blank check - where a row counts as blank only if the first 3 columns are all empty:
=FILTER('GL Detail'!A2:F3200,
('GL Detail'!A2:A3200 <> "") +
('GL Detail'!B2:B3200 <> "") +
('GL Detail'!C2:C3200 <> "") > 0)
The + operator acts as OR here. A row survives if any of those 3 cells has content.
When to use this: Source data refreshes automatically (Sheets import, connected range, or anything you don't own). You want downstream formulas like =SUMIFS(Staging!D:D, Staging!B:B, ">="&Assumptions!$B$3) to always hit clean data.
Method 2: Filter + Delete Blank Rows in Google Sheets (Fast, Use with Caution)
This permanently removes the rows. Fast and simple, but irreversible without a restore.
- Select the column most likely to be populated in non-blank rows (usually your date or account code column).
- Data → Create a filter.
- Click the filter dropdown on that column → Filter by condition → Is empty.
- Select all visible (blank) rows - Ctrl+Shift+End gets you to the last row fast.
- Right-click → Delete rows.
- Remove the filter.
On a 5,000-row import, this runs in under 2 minutes manually. The main risk: if your "blank" rows actually contain data in columns you didn't filter on (notes, flags, IDs in column Z), those get deleted silently.
When to use this: One-time data cleaning, you own the source, and you've verified the blank rows are genuinely empty across all columns. A quick =COUNTA(A2:Z2) helper column before you start will catch any rows that look blank but aren't.
Method 3: Apps Script to Delete Blank Rows (Repeatable)
If you're cleaning the same import every month - quarterly board pack prep, bank reconciliation exports, anything on a schedule - writing this once beats repeating Method 2 manually.
function deleteBlankRows() {
// Targets the 'GL Detail' sheet; change to match yours
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('GL Detail');
var data = sheet.getDataRange().getValues();
var rowsToDelete = [];
// Collect blank rows (check column A - adjust if needed)
for (var i = data.length - 1; i >= 0; i--) {
if (data[i][0] === '' || data[i][0] === null) {
rowsToDelete.push(i + 1); // +1 because Sheets rows are 1-indexed
}
}
// Delete in reverse order to avoid index shifting
rowsToDelete.forEach(function(row) {
sheet.deleteRow(row);
});
}
Run this from Extensions → Apps Script → Run. On a 3,200-row sheet with 280 blank rows, it takes 45-90 seconds. According to Google's Apps Script documentation, scripts have a 6-minute execution limit per run - so if you're cleaning something larger, batch the deletions or use Method 1 instead.
When to use this: Monthly cleanup scripts, shared models where someone else always triggers the import, or anywhere you want the same logic repeatable without manual steps.
Comparison: Which Method to Use
| Method | Destructive? | Speed | Best For |
|---|---|---|---|
| FILTER formula | No | Instant (auto-updates) | Refreshing imports, shared sources |
| Filter + Delete | Yes | < 2 min on 5K rows | One-time cleanup, owned data |
| Apps Script | Yes | 45-90 sec on 3.2K rows | Monthly scripts, repeatable cleanup |
The Right Pattern for a Monthly Close Model
Most close models have at least one tab that's a raw export - GL detail, AR aging, payroll extract. Those exports almost always come with blank separator rows baked in by whatever system generated them.
The cleanest pattern: keep the raw import on a locked tab, run FILTER into a staging tab, and point all your model formulas at staging. That way the import can refresh (or get replaced by a new export) without breaking anything.
=SUMIFS(Staging!D:D,
Staging!B:B, ">=" & Assumptions!$B$3,
Staging!C:C, Returns!$A7)
If you're running this cleanup interactively and want to preview exactly which rows will get deleted before committing - ModelMonkey's approval workflow highlights affected rows in the sheet before any deletion runs, which is useful when the blank row definition is ambiguous (sparse rows with a few populated columns, for example).
Try ModelMonkey free for 14 days - it works in both Google Sheets and Excel.