DataGrip Paging: Why the Grid's Row Count Isn't the Table's Size
DataGrip's result grid fetches rows in pages, so the count you see reflects what's been fetched — not the table's size. Here's how to page deliberately and when to push the work to SQL.
06 May 2026, 11:15 UTC

The row count in the grid is a page count, not a table count
Run SELECT * FROM orders in a DataGrip query console against a table you believe is small, and the grid may report a few hundred rows. It is easy to read that number as the size of the result. It usually is not. DataGrip's result grid retrieves rows in pages and shows what has been fetched so far; the rest of the result set is still sitting on the server.
That single behavior explains a lot of confusing debugging: a "missing" row that appears after you scroll or click the fetch-next-page control, a count that changes as you look at it, and exports that quietly contain less than you expected.
The thesis here is simple: treat the grid as a paging viewer for reading, not as a data mover. Set the page size to something you can justify, and push filtering, ordering and aggregation into SQL.
How paging shows up in the UI
Two things in the grid tell you paging is happening:
- A fetched-rows indicator, which counts rows already delivered to the client.
- A fetch-next-page or fetch-all action, which requests more rows from the server.
Page size lives in DataGrip's database data-editor settings. The exact label, menu path and default value change between releases, so open Settings/Preferences and read the current option in your installed version rather than trusting a number from a blog post — including this one.
One caution about mechanism: it is tempting to assume DataGrip rewrites your statement to append a LIMIT. That is not a safe assumption. Paging may be implemented at the JDBC driver level through fetch size or a max-rows setting, and behavior differs by driver and dialect. If the distinction matters to you, verify it rather than assume it (see the checks at the end).
A worked example: browsing versus bounded reads
Run these in a DataGrip query console against a connection where you have read permission on the table. Substitute real values for the named parameters.
First, the unbounded read:
-- Observe the fetched-rows indicator before and after fetching the next page
SELECT * FROM orders;
Note what the indicator says, then use the fetch-next-page action and note it again. The number grows; the statement did not change. That is client-side paging in action.
Now a bounded, repeatable read:
SELECT order_id, customer_id, created_at
FROM orders
ORDER BY created_at DESC, order_id DESC
LIMIT 200;
The ORDER BY plus a tiebreaker column makes the result deterministic — without it, two rows sharing a timestamp can swap positions between runs, which is exactly the kind of instability that makes paging look broken.
For the next page, offset pagination is the obvious move, but it gets slower as the offset grows because the database still has to walk the skipped rows. Keyset pagination avoids that:
SELECT order_id, customer_id, created_at
FROM orders
WHERE (created_at, order_id) < (:last_created_at, :last_order_id)
ORDER BY created_at DESC, order_id DESC
LIMIT 200;
Row-value comparison like this is supported by some databases (PostgreSQL and MySQL among them) and needs to be rewritten as an expanded predicate on others. Check your dialect before adopting it.
The trade-off you are actually choosing
| Page size | What you gain | What you give up |
|---|---|---|
| Small | Responsive IDE, low client memory, fast first paint | True cardinality is hidden; easy to misread during debugging |
| Large or fetch-all | Fuller picture of the result in one pass | Latency, client memory pressure, slower to recover from a bad query |
Neither is wrong. The mistake is leaving it on a default you never thought about, then drawing conclusions from a partial result.
There is also a task-shape question. Reading and moving data want different tools:
- Browsing — the grid with a modest page size is fine.
- Exporting — use DataGrip's export/extract features rather than scrolling a grid to the end.
- Bulk processing — run a scripted query and write the output where it needs to go; do not use the grid as a pipeline.
Paging and transactions
If auto-commit is off, an open result tab can sit inside an uncommitted transaction, and the server may hold resources — locks, a cursor, a snapshot — for as long as the tab stays open. Commit or roll back when you are done, and close result tabs you no longer need. This is not a DataGrip quirk; it is what an open transaction means.
Also worth stating plainly: the grid does not give you an authoritative table count, and it does not guarantee a consistent transactional snapshot of the table. Treat the number as "rows fetched," nothing more.
Checks you can run yourself
- Open Settings/Preferences, find the database data-editor section, and write down the exact page-size label and current value for your version.
- Run a plain
SELECTon a large table, record the fetched-rows indicator, fetch the next page, record it again. - Inspect the server's query log (or DataGrip's executed-statement output) to see whether a limited statement reached the database or whether paging happened at the driver level.
- Compare the plain
SELECTwith an explicitORDER BY ... LIMITon the same table and confirm which one gives a stable, bounded result. - Repeat the check against your specific JDBC driver, since fetch-size and max-rows handling varies.
The actionable version: pick a page size you can justify, prefer explicit ordering with a limit or keyset predicate for anything you will re-run, and reach for fetch-all only when you genuinely need every row.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.