Ways to Tweak Slow SQL Queries
helenanderson.co.nz
helenanderson.co.nz
Used that way — an outer join against a CTE — the planner was forced to generate a plan that produced a row for every candidate row in the table being used as input for the text-search, whether or not it would be needed, visiting nearly all of its pages.
Just by moving the CTE inline (that is: "LEFT JOIN ( #{ subquery } ) AS blah"), the query completed in 17ms.
This way, the planner could apply conditions from the rest of the query to the subplan from which text-search vector rows were being produced, such that it only pages containing rows it already knew it would care about would even be retrieved.
The rest of this article is pretty on-point, too.
Source: This stuff is my day job.
EDIT: As a counter-point, because they are incredibly useful, I've also had countless cases where rewriting a query to use CTEs was the several orders of magnitude win. This also wasn't the only possible fix; the qualifying conditions in the CTE could have been improved, eliminating the extra work where it would have occurred.
Like all things computers, the real answer is, "it depends..."
Yes, that's coming.
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit...
This is what makes the job exist. If it was algorithmic we'd already have been replaced by EXPLAIN
[0] https://www.depesz.com/2019/02/19/waiting-for-postgresql-12-...
That is: they mostly aren't a fence any more, but can be when you want them to, and sometimes still must be.
> Using wildcards at the start and end of a LIKE will slow queries down
That's only true if you don't have the right indexes in place. A wildcard at the end can use a regular (btree) index. Even a LIKE query with wildcards in other places can be fast with a trigram index.
https://niallburkley.com/blog/index-columns-for-like-in-post...
depending on the locale your db is in, it might be a little less "regular". recently fell in that trap.
http://blog.cleverelephant.ca/2016/08/pgsql-text-pattern-ops...
This is not performance-related in most cases. Unless the bottleneck is in the amount of data being transferred to a client over a network connection and there is a large amount of columns, or you have an index which matches the limited column list query exactly, there would be no performance difference in SELECT * vs SELECT <column list>.
In columnar dbs it does matter because the less columns you select the less data gets accessed on disk. However, this does not hold true for row stores because data is stored in such a way that the whole row gets accessed no matter how many columns get specified in the query.
There are many other good reasons why SELECT * is acceptable only in development queries, but performance is not one of them.
In fact, I'd go so far as to say that the practice of selecting all columns makes it practically impossible to make use of covering indexes for high impact queries.
The way I read the article, it seems to suggest that by removing rows from the select list would somehow always result in better performance. In my opinion, in the majority of cases there will be no measurable impact on performance.
In some specific cases, however, yes, it can make a big difference. In postgres it also seems like selecting all columns explicitly, but out of order can be detrimental to performance:
https://www.postgresql.org/message-id/5562FE06.9030903@lab.n...
So, it depends :)
Clearly, this type of index is both expensive to maintain, and the benefits are lessened if you include every column in a table in the covering index. So, to actually have real benefits, you need to have carefully crafted queries which only select what they need, and these being matched to carefully crafted covering indexes. Basically, you cannot have these - which are one of the better weapons you have for performance - if you always select everything.
Otherwise it does not matter how many columns you select, because you will have to read all the columns for every row anyway.
It does matter. An index can actually contain a cache of values for some columns. This is done with INCLUDE statement.
CREATE INDEX idx
ON book ( author_id )
INCLUDE ( book_title )
This select will use only index: select author_id, book_title
from books
where author_id = 123
This select will use index, and then have to follow and fetch data out of rows: select *
from books
where author_id = 123
https://use-the-index-luke.com/blog/2019-04/include-columns-...Selecting only the columns you need just prevents the database from having to go back to the clustered index to retrieve the remaining information that wasn't included on the index.
Selecting * from a table has no impact on the indexes used. But it can be expensive to need to go back to the clustered index to get the additional information.
This is especially important when querying views (or inline functions, if your DBMS supports them) built on top of several other layers of views.
This is not true, at least not with the likes of MS SQL Server / postgres / Oracle / etc. If the query can be satisfied with just what is in an index the clustered index or heap (where all the row data is together) does not need to be referenced. Worse: if you have off-page data (NVARCHAR(MAX) columns with values that aren't very short, to give one common example in the case of SQL Server) then not only is the engine reading the CI/heap unnecessarily to serve your query but it is potentially making extra unnecessary per-value page reads on top of that.
Also for more complex queries that cause the building of intermediate results in memory or worse on disk, anything where you see a sort step in the query plan for instance, you are increasing the amount of data that needs to be spooled in this way. In some circumstances this might happen to the date more than once during a single run of your query.
Or if columns are actually the result of UDFs - they will be getting run per value per row even though the result is not needed, and that can also affect choices the query planner makes wrt parallelism options. You might not know you query is "more complex" either: do you know for sure that you are referencing a simple table or a view that is doing some jiggery-pokery to derive values? It might be a simple table now but that may change in later versions of the model/application, and now you have that extra computation happening for values you might not actually care about. It doesn't have to be something as complex as a table being replaced by a view that is doing extra work: the entity might later have a free-form note record attached to it in the form of a VARHCAR(MAX) column and those extra off-page reads are hitting your query's performance server-side (not just in terms of what travels over the network) when you don't actually need the results of those reads.
> There are many other^H^H^H^H^H good reasons why SELECT is acceptable only in development queries*
I'd say "to be avoided where possible outside of dev/test queries" as there are always exceptions where it isn't possible due to [bad] design elsewhere, but yes.
> but performance is not one of them.
This I disagree with. It can definitely affect performance. And anyway, it might not affect performance on this particular query, at least not by a measurable amount, being selective about what you select is a good habit to train into yourself.
My issue with this advice is that it enforces the idea that "less columns in select list" = "less data accessed" _in all cases_, which as we all agree is not true. Even more so if you have a relatively well designed database, with no crazy amount of views on views (or any for that matter), UDFs, huge columns with binary data, etc, etc.
PS: "...result of UDFs - they will be getting run per value per row..." -> not always.. Inline tvfs get expanded, so you should probably be quite careful with all other user defined functions in SQL Server anyway.
I for one try to avoid absolutes like that. "less columns in select list = potentially less data accessed & processed" is a better wording. Along with "and there are no cases, unless there is a QP bug around the matter than I'm unaware of, where it changes things for the worse" if I'm feeling more wordy.
The problem with that though is the some (I've worked with some difficult people!) see the "potentially" as a reason to just drop in the * "for now" - their reasoning being it is a premature optimisation. Why type the extra if it won't definitely improve performance, despite the warnings that while it might not affect performance now it may in future, possibly due to changes elsewhere making this a trickier fix point to find, and even ignoring the performance issue it is good practise for other reasons.
> more so if you have a relatively well designed database
Oh, how I long for such nirvana!
> Inline tvfs get expanded
But scalar functions do not, well not until you are using 2019 which is due for release soon, and even then only some can be. And even if your SVF doesn't result in any reads, so inlining in this sense doesn't matter, there is extra CPU work going on.
(assuming SQL Server of course, details will of course differ elsewhere)
EXPLAIN (costs off)
SELECT relname, relpages
FROM pg_class
WHERE relname IN ('pg_class');
QUERY PLAN
---------------------------------------------------------
Index Scan using pg_class_relname_nsp_index on pg_class
Index Cond: (relname = 'pg_class'::name)
(2 rows)
In general, I'd like to echo other's comments, learning how to use EXPLAIN and how to interpret query plans is the single most important tool, as it allows you to verify your hypothesis instead of relying on rules-of-thumb.In my case `column IN ('value1', 'value2')` was converted to an array like this: `((column)::text ANY ('{value1,value2}'::text[]))`, an index on the column was completely ignored and PG performed a sequential scan.
After changing it to `column = ANY (VALUES ('value1'),('value2'))` it created a table in memory and used it when performing an index scan.
I have feeling this is maybe some kind of a bug in query planner? This is on PG 9.6.8.
In the bad plan a sequential scan was performed and then it removed over 4 million rows (Rows Removed by Filter: 4562849)
Edit: here is the query planner's output: https://explain.depesz.com/s/eVXI
After rewriting the query (change IN into ANY(VALUES( and adding an index to the "seven" table): https://explain.depesz.com/s/b4kU
You said the index was ignored, but based on that plan output the planner made the right choice since the filter on the “sierra_zulu” table (only one there with an ANY so I assume that’s the one) matches over 90% of the rows. An index scan on that would be far worse.
Adding an index on a different table allowed the driving side of the join to be different, and, with already prefiltered data allowed the other index to be useful as well (fewer index lookups). I’d bet with that index addition your IN clause works just fine.
So that change of query alone reduced query time from 35s to ~5s, after that the seven table become the bottleneck and adding an index there reduced the whole query down to 750ms.
I should have saved the intermediate plan to illustrate it, but it essentially looked same as the second plan, except there was a sequence scan on seven table.
- code with a high row count table variable when a temp table would give more accurate execution plans due to better cardinality estimates.
- ultra-complex join criteria with lots of OR logic that performed better as a UNION
- lazy function calling, where a developer used a function that did more than they needed and could be replaced with a simple join to a table
- looped calls to insert procs that could be done in bulk
There was another entire category of problem that I guess would be described as "I hate sets" and was mostly attributable to one former employee. These were recognizable immediately upon opening the code and were a nightmare.
Care to elaboarate? I need a pick-me-up today.
- Get a list of x in a table var
- while loop through x to build another list of y
- while loop through y and update z one row at a time
All of that rather than updating with a join.
Some of his other patterns were reusing variables for different things in a proc, using inefficient functions in a way that they executed once per row before the result set was really reduced much, nesting those functions, and using loops any chance he got.
You'd open up these slow, 500-700 line monster procs and have to figure out what they were doing and refactor them, but it was nearly illegible and there were no tests for them. Really a great reminder of what happens when code reviews aren't done.
I imagine this is/was a simple optimization for DBMS to implement, but I would love to be corrected otherwise...
I thought databases flatten all view layers into a single SQL statement before executing. Aka no performance impact except for a couple milliseconds to flatten out the views.
It's not clear to me why IN () can't do an index scan or hash join?
This was a limit of the optimizer previously (IN clauses are broken down into AND/OR groups to prove inferences but only if <= 100 items).
But Postgres 12 includes a patch I wrote so that the optimizer can prove the NOT NULL inference directly from an array operator of any size.
Unfortunately we are stuck with 9.6.x since before my time my company decided to use AWS Aurora, currently there's no easy path to do major version upgrade from 9.6.x and anyway 10.x is the most recent available version :( but any information why is this happening would be appreciated.
This is a problem when trying to test an index on a small dataset. It may look like the index is not useful, when infact it will be useful.
> ”This is written with PostgreSQL in mind but if you’re using something else, these may still help, give them a try.