考虑下表:
CREATE TABLE foo (
id INT PRIMARY KEY,
effective_date DATETIME NOT NULL UNIQUE
)
给定一组日期D,如何从foo中获取所有行,其中effective_date的最大值小于单个查询中D中的每个日期?
为简单起见,假设每个日期只有一个匹配的行。
假设foo有以下行。
---------------------
| 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|
---------------------
如果您获得D = {2013-02-20,2013-06-30,2013-12-19},查询应返回以下内容:
---------------------
| id |effective_date|
---------------------
| 1 | 2013-02-03|
| 4 | 2013-05-11|
| 6 | 2013-12-08|
如果D只有一个元素,比如D = {2013-06-30},你可以这样做:
SELECT *
FROM foo
WHERE effective_date = SELECT MAX(effective_date) FROM foo WHERE effective_date < 2013-06-30
如果D的大小大于1,如何在IN子句中指定D,那么如何推广此查询?
答案 0 :(得分:3)
实际上,您的问题是 - 在大多数情况下,您有一个值列表,在MySQL中将其视为行 - 而不是作为集合。也就是说 - 可能的解决方案之一是在应用程序中正确生成您的集合,使其看起来像:
SELECT '2013-02-20'
UNION ALL
SELECT '2013-06-30'
UNION ALL
SELECT '2013-12-19'
- 然后在JOIN
内使用生成的集合。此外,如果MySQL可以接受ANY
子查询中的静态列表(例如IN
关键字),那将会很棒,但它不能。 ANY
还要求设置行,而不是列表(将被视为包含N
列的行,其中N
是列表中项目的数量。)
幸运的是,在您的特定情况下,您的问题有一个重要的限制:列表中的项目可能不会比foo
表中的行更多(否则没有任何意义)。因此,您可以动态构建该列表,然后使用它:
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
- 您的列表将通过ELT()
自动迭代,因此您可以直接将其传递给查询而无需任何其他重组。请注意,此方法将遍历所有foo
记录以生成行集,因此它将起作用 - 但在应用程序中执行这些操作可能在性能方面更有用。
您的表格的演示可以找到here。
答案 1 :(得分:2)
也许这会有所帮助:
SELECT *
FROM foo
WHERE effective_date IN
(
(SELECT MAX(effective_date) FROM foo WHERE effective_date < '2013-02-20'),
(SELECT MAX(effective_date) FROM foo WHERE effective_date < '2013-06-30'),
(SELECT MAX(effective_date) FROM foo WHERE effective_date < '2013-12-19')
)
结果:
---------------------
| id |effective_date|
---------------------
| 1 | 2013-02-03| -- different
| 4 | 2013-05-11|
| 6 | 2013-12-08|
更新 - 12月6日
创建程序:
DELIMITER $$
USE `test`$$ /*change database name*/
DROP PROCEDURE IF EXISTS `myList`$$
CREATE PROCEDURE `myList`(ilist VARCHAR(100))
BEGIN
/*var*/
/*DECLARE ilist VARCHAR(100) DEFAULT '2013-02-20,2013-06-30,2013-12-19';*/
DECLARE delimeter VARCHAR(10) DEFAULT ',';
DECLARE pos INT DEFAULT 0;
DECLARE item VARCHAR(100) DEFAULT '';
/*drop temporary table*/
DROP TABLE IF EXISTS tmpList;
/*loop*/
loop_item: LOOP
SET pos = pos + 1;
/*split*/
SET item =
REPLACE(
SUBSTRING(SUBSTRING_INDEX(ilist, delimeter, pos),
LENGTH(SUBSTRING_INDEX(ilist, delimeter, pos -1)) + 1),
delimeter, '');
/*break*/
IF item = '' THEN
LEAVE loop_item;
ELSE
/*create temporary table*/
CREATE TEMPORARY TABLE IF NOT EXISTS tmpList AS (
SELECT item AS sdate
);
END IF;
END LOOP loop_item;
/*view*/
SELECT * FROM tmpList;
END$$
DELIMITER ;
致电程序:
CALL myList('2013-02-20,2013-06-30,2013-12-19');
查询:
SELECT
*,
(SELECT MAX(effective_date) FROM foo WHERE effective_date < sdate) AS effective_date
FROM tmpList
结果:
------------------------------
| sdate |effective_date|
------------------------------
| 2013-02-20 | 2013-02-03 |
| 2013-06-30 | 2013-05-11 |
| 2013-12-19 | 2013-12-08 |
答案 2 :(得分:0)
首先是坏方法(没有有序的分析函数,或者rank / row_number)
sel tmp.min_effective_date, for_id.id
from
(
Sel crossed.effective_date,max(SRC.effective_date) as min_effective_date
from
foo as src
cross join
foo as crossed
where
src.effective_date <cross.effective_date
and crossed.effective_date in
(given dates here)
group by 1
) tmp inner join foo as for_id on
tmp.effective_date =for_id.effective_date
接下来,使用row_number
SEL TGT.id, TGT.effective_date
(Sel id, effective_date, row_number() over(order by effective_date asc) as ordered
) SRC
INNER JOIN
(Sel id, effective_date, row_number() over(order by effective_date asc) as ordered ) TGT
on
src.ordered+1=TGT.ordered
where src.effective_date in (given dates)
具有有序分析功能:
sel f.id, tmp.eff
foo as f inner join
(SEL ID, max(effective_date) over(order by effective_date asc ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) as eff
from foo
) TMP
on f.id = tmp.id
where f.effective_date in (given dates)
and tmp.eff is not null
上面的查询假设需要选择id,并且源中的id不遵循与日期相同的序列(例如,升序)。否则,您可以立即使用有序的分析功能。