【问题标题】:Is it possible to search for a year in a date range with MySQL是否可以使用 MySQL 在日期范围内搜索一年
【发布时间】:2017-02-21 08:49:24
【问题描述】:

给出的是包含一段时间的数据,跨越数年。就像这样:

| ID | Name               | Alive                 |
|----|--------------------|-----------------------|
| 1  | Washington, George | 1732-02-22/1799-12-14 |
| 2  | Adams, John        | 1735-10-30/1826-07-04 |
| 3  | Jefferson, Thomas  | 1743-04-13/1826-07-04 |
  …

是否可以通过搜索中间日期(在所有字段中,仅一年)(如搜索词 1788)产生结果的方式将这些数据存储在 MySQL 中?

我正在寻找的是这样的:

CREATE TABLE t (
    id INT NOT NULL,
    name VARCHAR(30),
    alive DATERANGE
);

SELECT * FROM t WHERE * LIKE '%1788%'

我看到的唯一解决方案是添加另一个包含年份列表的列 (1732,1733,...),但我想有更好的解决方案。我需要一个或两个字段中的日期吗?我需要什么列类型才能使它起作用?我可以在该列中指定日期范围(例如1155/1227)还是必须在插入之前重写它们(例如1155-01-01/1227-12-31)?

边界匹配也应返回。搜索字符串 1799 仍应返回 George Washington,即使他在 1 月 1 日至 12 月 31 日(含)期间都不在世。我想这很简单,因为它已经是一个字符串匹配了。

【问题讨论】:

  • 您应该将开始日期和结束日期放在两个单独的列中,并使用正确的日期列类型而不是文本。
  • 我同意@CBroe,你可以select * from t where YEAR(startDate) = 1788 OR YEAR(endDate) = 1788
  • @TheFlash 您应该将您的评论作为答案并将= 替换为><,这是我正在寻找的范围
  • @Paramaeleon 我添加了答案,谢谢

标签: mysql date search


【解决方案1】:

如果您可以编辑数据,那么我建议将其更改为 Born & Died 字段,否则我们可以将 LEFTINSTR 函数用于 Born,将 SUBSTRING_INDEX 函数用于 Died。

SELECT ID, Name, Alive, 
LEFT([ColName],INSTR([Alive],"/")-1) AS Born, 
SUBSTRING_INDEX(Alive,'/',-1) AS Died
FROM t

将划分出生日期和死亡日期:

| ID | Name               | Alive                 | Born       | Died       |
|----|--------------------|-----------------------|------------|------------|
| 1  | Washington, George | 1732-02-22/1799-12-14 | 1732-02-22 | 1799-12-14 |
| 2  | Adams, John        | 1735-10-30/1826-07-04 | 1735-10-30 | 1826-07-04 |
| 3  | Jefferson, Thomas  | 1743-04-13/1826-07-04 | 1743-04-13 | 1826-07-04 |

那么你可以使用:

WHERE Alive LIKE '%1788%'

搜索日期。

或单独出生:

WHERE LEFT([ColName],INSTR([Alive],"/")-1) LIKE '%1788%'

死了:

WHERE SUBSTRING_INDEX(Alive,'/',-1) LIKE '%1788%'

或者,如果您只是想要 Born 和 Died 字段中的年份,请使用额外的 LEFT 函数:

SELECT ID, Name, Alive, 
LEFT(LEFT([ColName],INSTR([Alive],"/")-1),4) AS Born, 
LEFT(SUBSTRING_INDEX(Alive,'/',-1),4) AS Died
FROM t

这会给你:

| ID | Name               | Alive                 | Born | Died |
|----|--------------------|-----------------------|------|------|
| 1  | Washington, George | 1732-02-22/1799-12-14 | 1732 | 1799 |
| 2  | Adams, John        | 1735-10-30/1826-07-04 | 1735 | 1826 |
| 3  | Jefferson, Thomas  | 1743-04-13/1826-07-04 | 1743 | 1826 |

编辑:

您可以反过来使用BETWEEN 函数。

SELECT ID, Name, Alive, 
LEFT(LEFT([ColName],INSTR([Alive],"/")-1),4) AS Born, 
LEFT(SUBSTRING_INDEX(Alive,'/',-1),4) AS Died
FROM t
WHERE 1788 BETWEEN LEFT(LEFT([ColName],INSTR([Alive],"/")-1),4) AND LEFT(SUBSTRING_INDEX(Alive,'/',-1),4)

【讨论】:

  • 最后一个最接近我需要的,当然WHERE Born >= '%1788%' AND Died <= '%1788%'是我在范围中查找的意思。
  • @Paramaeleon 添加了另一个查询
【解决方案2】:

我需要一个或两个字段中的日期

肯定有两个,birthdeath,并使用谓词 BETWEEN ... AND ... 进行搜索。它比在每次查询时将一个字段分成两部分成本更低,并且可以更好地利用索引。

我需要的列类型是什么

这更棘手。我通常肯定会同意 cmets 说您必须使用 date 字段,这是出于各种众所周知的充分理由。但是,从您的问题中可以明显看出,您只对年份感兴趣,而实际上忽略了实际日期;此外,您正在处理可能不完整的历史数据:在这种情况下,通常会丢失几天甚至几个月;此类不完整的日期可以存储在date 字段中,但在某些操作上返回NULL,这可能会产生问题;当你有一个date 字段时,你不能在年份上创建索引,所以你的查询都是全表扫描。简而言之,在您的特殊情况下,我会选择 SMALLINT UNSIGNED 来保存这些年,而 CHAR(5) 来存储不太有用的月份和日期信息,以防万一您可能需要它未来,与CAST(CONCAT(year,'-', month_and_day) AS DATE) 建立一个真正的即时约会。 总之,这是我提出的设计:

CREATE TABLE t (
    id INT NOT NULL,
    name VARCHAR(30),
    birth_year SMALLINT UNSIGNED,
    birth_md CHAR(5),
    death_year SMALLINT UNSIGNED,
    death_md CHAR(5) 
);

CREATE INDEX t_ndx ON t(birth_year, death_year);

SELECT * FROM t WHERE 1788 BETWEEN birth_year AND death_year;

【讨论】:

    【解决方案3】:

    就像@CBroe 建议的那样 - 你应该有两列(startDate & endDate OR bornDate & DeathDate),然后你可以这样写你的查询:

    select * from t where YEAR(startDate) >= 1788 OR YEAR(endDate) <= 1788
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-25
      • 1970-01-01
      相关资源
      最近更新 更多