Search code examples
sqlperformancesqliteunionexplain

SQLite: optimize query with union


This is my table definition:

create table User (
    userUuid text not null primary key,
    username text not null,
    thisUserBlockedCurrentUser int not null,
    currentUserBlockedThisUserTsCreated int not null,
    searchScreenScore int,
    recentSearchedTsCreated int,
    friends int not null
);

create index User_X on User(thisUserBlockedCurrentUser, friends);

And this is my query + plan:

explain query plan
select *
from (select User.*
      from User
      where friends = 1
        and User.currentUserBlockedThisUserTsCreated is null
        and User.thisUserBlockedCurrentUser = 0
        and User.username != ''
      union
      select User.*
      from User
      where recentSearchedTsCreated is not null
        and User.currentUserBlockedThisUserTsCreated is null
        and User.thisUserBlockedCurrentUser = 0
        and User.username != '')
order by case when friends = 1 then -2 when recentSearchedTsCreated is not null then -1 else searchScreenScore end,
         username;

CO-ROUTINE (subquery-2)
COMPOUND QUERY
LEFT-MOST SUBQUERY
SEARCH User USING INDEX User_X (thisUserBlockedCurrentUser=? AND friends=?)
UNION USING TEMP B-TREE
SEARCH User USING INDEX User_X (thisUserBlockedCurrentUser=?)
SCAN (subquery-2)
USE TEMP B-TREE FOR ORDER BY

So the index is used, but there is still a scan and a b-tree involved in the order by. I tried getting rid of them by adding more indexes, but I don't get it working.

Any ideas of an index which gets rid of the scan?


Solution

  • Your query can be simplified to:

    SELECT *
    FROM User
    WHERE (friends = 1 OR recentSearchedTsCreated IS NOT NULL) 
      AND currentUserBlockedThisUserTsCreated IS NULL 
      AND thisUserBlockedCurrentUser = 0 
      AND username <> ''
    ORDER BY CASE 
               WHEN friends = 1 THEN -2 
               WHEN recentSearchedTsCreated IS NOT NULL THEN -1 
               ELSE searchScreenScore 
             END,
             username;
    

    and the query plan will be:

    SEARCH User USING INDEX User_X (thisUserBlockedCurrentUser=?)
    USE TEMP B-TREE FOR ORDER BY
    

    I don't see how you can get better than B-TREE in the ORDER BY clause since you are using a custom expression to sort by.

    See the demo.