Search code examples
sqlsqlitesubquery

Multiple subquery criteria or looping in SQLite


Is it possible to perform a "foreach"-like query in a single SQL statement? My specific implementation is using SQLite, so suggestions with compatible syntax are preferred.

Example: Let's say we have a table similar to the following:

simulation_id  |  sprite_id  |  time | x  |  y
1               1             0.0     0.0  0.0
1               2             0.0     5.5  1.6
1               1             1.0     0.1  0.0
1               2             1.0     5.5  1.5
1               3             1.0     9.9  4.1
1               1             2.0     ...
1               2             2.0
1               3             2.0
2               1             0.0
2               1             1.0
2               1             2.0
2               2             2.0
2               1             3.0
2               2             3.0
2               3             3.0
...

The key takeaway from the table is that not all "sprite_id" entries may be instantiated at t=0.0.

I would like to retrieve the positions for all sprites at the first instance sprite_id=3 appears in each simulation. So, my ideal query would retrieve the positions for sprites 1, 2, and 3 at t=1.0 for simulation_id=1, and t=3.0 for simulation_id=2.

I believe the query would be something along the lines of:

SELECT simulation_id, sprite_id, time, x, y 
FROM locations WHERE time = (SELECT MIN(time) FROM locations WHERE sprite_id = 3)

...however I would need this run for each simulation_id in the table; is this even possible in a single SQL query?


Solution

  • Your can use a correlated subquery in the WHERE clause:

    SELECT l.*
    FROM locations l
    WHERE l.sprite_id = 3
    AND time = (
      SELECT MIN(time) 
      FROM locations 
      WHERE simulation_id = l.simulation_id AND sprite_id = l.sprite_id
    )
    

    Or with ROW_NUMBER() window function:

    SELECT simulation_id, sprite_id, time, x, y
    FROM (
      SELECT *, ROW_NUMBER() OVER (PARTITION BY simulation_id ORDER BY time) rn 
      FROM locations 
      WHERE sprite_id = 3
    )
    WHERE rn = 1
    

    See the demo.
    Results:

    > simulation_id | sprite_id | time   | x    | y   
    > ------------: | --------: | ---:   | :--- | :---
    >             1 |         3 |    1.0 | .... | ....
    >             2 |         3 |    3.0 | .... | ....