Skip to content
Advertisement

How to iterate rows with known start position and mixed column ordering, exactly as if walking the corresponding index?

I have a given, unmodifiable table design which resembles:

I simply want to iterate rows from this table in the order c1 asc, c2 desc, starting at a known position, e.g., ('foo', 5). The query must be efficient, i.e. the query planner must be able to take advantage of the unique index and do an index scan if the table is large enough. This should require at most log(n) + 2 * limit reads for large tables.

Examples (fixed after edit):

You’d think something like this should be trivial, but apparently with SQL this is harder than it should be!? Consider the following incorrect (!) queries which I’m only listing to avoid getting them as an answer 😉

Advertisement

Answer

I suspect that the trickiest part will stopping it from using the index. Which is to say just walking the index from one end discarding stuff that doesn’t match the filter.

The planner isn’t smart enough (afaict in 2021) to spot that the OR there has a strong correlation on each side. We know that we are actually just trying to get a subset of the table, but the planner – nope.

So – one thing you can do with a tricky OR is to split it into two subqueries and use UNION ALL.

I’ve written this as a CTE (the WITH clauses) because I think it looks more readable that way. It should be identical to actual subqueries for the planner though.

That gives me (EXPLAIN ANALYSE ...) a plan something like:

You incur an extra sort I’m afraid, but you can’t guarantee your ordering otherwise.

Obviously this doesn’t give your your odd queries that should be returning true or an empty set. That’s because they don’t really make any sense in the sense of asking for a subset of the table.

Test data used for above query plan:

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement