Return rows with maximum date less than each value in a set of dates in SQL

mysql, sql

Solution

Actually, your problem is - that you have a list of values, which will be treated in MySQL as row - and not as a set - in most cases. That is - one of possible solutions is to generate your set properly in application so it will look like:

SELECT '2013-02-20'
UNION ALL
  SELECT '2013-06-30'
UNION ALL
  SELECT '2013-12-19'

-and then use produced set inside `JOIN`. Also, that will be great, if MySQL could accept static list in `ANY` subqueries - like for `IN` keyword, but it can't. `ANY` also expects rows set, not list (which will be treated as row with `N` columns, where `N` is count of items in your list).

Fortunately, in your particular case your issue has important restriction: there could be no more items in list, than rows in your `foo` table (it makes no sense otherwise). So you can dynamically build that list, and then use it like:

SELECT 
  foo.*, 
  final.period 
FROM 
  (SELECT 
    period, 
    MAX(foo.effective_date) AS max_date 
  FROM 
    (SELECT 
      period 
    FROM 
      (SELECT 
        ELT(@i:=@i+1, '2013-02-20', '2013-06-30', '2013-12-19') AS period 
      FROM 
        foo 
          CROSS JOIN (SELECT @i:=0) AS init) AS dates 
      WHERE period IS NOT NULL) AS list 
      LEFT JOIN foo 
        ON foo.effective_date<list.period 
    GROUP BY period) AS final 
    LEFT JOIN foo 
      ON final.max_date=foo.effective_date

-your list will be automatically iterated via `ELT()`, so you can pass it directly to query without any additional restructuring. Note, that this method, however, will iterate through all `foo` records to produce row set, so it will work - but doing the stuff in application may be more useful in terms of performance.

The demo for your table can be found here.

Problem

Consider the following table: ``` CREATE TABLE foo ( id INT PRIMARY KEY, effective_date DATETIME NOT NULL UNIQUE ) ``` Given a set of dates D, how do you fetch all rows from foo whose effective_date is the greatest value less than each date in D in a single query? For simplicity, assume that each date will have exactly one matching row. Suppose foo has the following rows. ``` --------------------- | id |effective_date| --------------------- | 0 | 2013-01-07| --------------------- | 1 | 2013-02-03| --------------------- | 2 | 2013-04-19| --------------------- | 3 | 2013-04-20| --------------------- | 4 | 2013-05-11| --------------------- | 5 | 2013-06-30| --------------------- | 6 | 2013-12-08| --------------------- ``` If you were given D = {2013-02-20, 2013-06-30, 2013-12-19}, the query should return the following: ``` --------------------- | id |effective_date| --------------------- | 1 | 2013-02-03| | 4 | 2013-05-11| | 6 | 2013-12-08| ``` If D had only one element, say D = {2013-06-30}, you could just do: ``` SELECT * FROM foo WHERE effective_date = SELECT MAX(effective_date) FROM foo WHERE effective_date < 2013-06-30 ``` How do you generalize this query when the size of D is greater than 1, assuming D will be specified in an IN clause?

Original source

Related problems