androidsqlandroid-room

Custom Room SQL query


I have a query in room DAO, which finds stations based on characters.

@Query("""
    SELECT code, name
    FROM RoomStations 
    WHERE 
    :input IS NULL
    OR 
    LOWER(code) LIKE LOWER(:input)
    OR
    LOWER(name) LIKE LOWER(:input || '%')
    LIMIT 3
""")

it works fine, and returns what I need.

I want it to look, input in code first, if not found then check the name, if found then skip the name.

when input= BE it returns

BAP- BELAPUR,

BAY- BELLARY

WHAT I WANT IT TO DO

BE-BAREILLY

How Can I get this result?


Solution

  • If the interpretation of I want it to look, input in code first, if not found then check the name, if found then skip the name. is correct then the following may be what you want:-

    @Query("""
        SELECT code,name 
            FROM roomstations
            WHERE :input IS NULL
            OR code LIKE :input
            OR (name LIKE :input||'%' AND (SELECT count(*) FROM roomstations WHERE code LIKE :input) = 0)
        LIMIT 3
        ;
    """)
    

    Perhaps consider the following demonstration from an SQLite tool (the suggested way to put together more complex queries):-

    DROP TABLE IF EXISTS roomstations; /* just in case make sure the environment is clean */
    CREATE TABLE IF NOT EXISTS roomstations (code TEXT,name TEXT); /* create the table for the environment */
    /* Addd some testing data */
    INSERT INTO roomstations VALUES
        ('BAP','BELAPUR'),
        ('BAY','BELLARY'),
        ('BE','BAREILLY'),
        ('BAP','XXXX'),
        ('BAY','ZZZZ'),
        ('BE','YYYY'),
        ('BAP','WWWW')
    ;
    /* RESULT 1*/
    /* Original query from the question */
    SELECT code, name
        FROM RoomStations 
        WHERE 
        'BE' /*<<< :input*/ IS NULL
            OR 
            LOWER(code) LIKE LOWER('BE' /*<<< :input*/)
            OR
            LOWER(name) LIKE LOWER('BE' /*<<< :input*/|| '%')
        LIMIT 3
    ;
    /* RESULT 2 */
    /* The answer with hardcoded value (would be bound i.e. for room :input instead of the actual value)*/
    SELECT code,name 
        FROM roomstations
        WHERE 'BE' /*<<< :input*/ IS NULL
        OR code LIKE 'BE' /*<<< :input*/
        OR (name LIKE 'BE'||'%' /*<<< :input*/ AND (SELECT count(*) FROM roomstations WHERE code LIKE 'BE' /*<<< :input*/) = 0)
    LIMIT 3
    ;
    /* RESULT 3 */
    /* to cater for testing different values, use a CTE  for the value so it only needs to be changed once */
    /* So instead of :input the expression (SELECT * FROM cte_parm) is used */
    /* in this case value is BE (so result should match the previous result) */
    WITH 
        cte_parm AS (SELECT 'BE')
    SELECT code,name
    FROM roomstations
    WHERE 
        (SELECT * FROM cte_parm) IS NULL
        OR code LIKE (SELECT * FROM cte_parm)
        OR (name LIKE (SELECT * FROM cte_parm)||'%' /*<<< :input*/ AND (SELECT count(*) FROM roomstations WHERE code LIKE (SELECT * FROM cte_parm) /*<<< :input*/) = 0)
    LIMIT 3
    ;
    /* RESULT 4 */
    /* this time value is BA (there is no BA code and just the single name that starts with BA) */
    WITH 
        cte_parm AS (SELECT 'BA')
    SELECT code,name
    FROM roomstations
    WHERE 
        (SELECT * FROM cte_parm) IS NULL
        OR code LIKE (SELECT * FROM cte_parm)
        OR (name LIKE (SELECT * FROM cte_parm)||'%' AND (SELECT count(*) FROM roomstations WHERE code LIKE (SELECT * FROM cte_parm)) = 0)
    LIMIT 3
    ;
    /* RESULT 5 */
    /* value is null so ALL rows (LIMIT removed) */
    WITH 
        cte_parm AS (SELECT null)
    SELECT code,name
    FROM roomstations
    WHERE 
        (SELECT * FROM cte_parm) IS NULL
        OR code LIKE (SELECT * FROM cte_parm)
        OR (name LIKE (SELECT * FROM cte_parm)||'%' AND (SELECT count(*) FROM roomstations WHERE code LIKE (SELECT * FROM cte_parm)) = 0)
    /*LIMIT 3 removed to show all */
    ;
    /* Cleanup the test environment */
    DROP TABLE IF EXISTS roomstations;
    

    The above results in:-

    RESULT 1 your query

    enter image description here

    RESULT2 Just the rows where the code matches BE

    enter image description here

    RESULT3 same as result 2 but testing the use the the value BE from the CTE

    enter image description here

    RESULT 4 with the value as BA (node code BA so names that start with BA)

    enter image description here

    RESULT 5 with NULL as the value i.e. all rows (as LIMIT removed)

    enter image description here