我有一个带有 PostgreSQL 数据库的 Ruby on Rails 应用程序;几个表具有 created_at 和 updated_at 时间戳属性。显示时,这些日期会以用户的语言环境进行格式化;例如,时间戳 2009-10-15 16:30:00.435
变为字符串 15.10.2009 - 16:30
(此示例的日期格式为 dd.mm.yyyy - hh.mm
).
要求是用户必须能够按日期搜索记录,就好像它们是在当前语言环境中格式化的字符串一样。例如,搜索 15.10.2009
将返回日期为 2009 年 10 月 15 日的记录,搜索 15.10
将返回日期为任何一年的 10 月 15 日的记录,搜索 15
将返回匹配 15 的所有日期(无论是日、月还是年)。由于用户可以使用日期的任何部分作为搜索词,因此无法将其转换为日期/时间戳以进行比较。
一种(慢速)方法是检索所有记录,格式化日期,然后对其执行搜索。这可以通过首先只检索 id 和日期,执行搜索,然后获取匹配记录的数据来加快;但对于大量行来说它仍然可能很慢。
另一种(与数据库无关的)方法是使用 PostgreSQL 函数或运算符将日期转换/格式化为数据库中的正确格式,并让数据库进行匹配(使用 PostgreSQL 正则表达式运算符或诸如此类)。
有没有办法以与数据库无关的方式高效地执行此操作(无需获取所有行)?还是您认为我走错了方向,应该以不同的方式处理问题?
最佳答案
根据 Carlos 的回答,如果您在所有日期和日期部分字段上都有索引,这应该允许您进行所有搜索而无需全表扫描。基于函数的索引对于日期部分列会更好,但我没有使用它们,因为这不应该是特定于数据库的。
CREATE TABLE mytable (
col1 varchar(10),
-- ...
inserted_at timestamp,
updated_at timestamp);
INSERT INTO mytable
VALUES
('a', '2010-01-02', NULL),
('b', '2009-01-02', '2010-01-03'),
('c', '2009-11-12', NULL),
('d', '2008-03-31', '2009-04-18');
ALTER TABLE mytable
ADD inserted_at_month integer,
ADD inserted_at_day integer,
ADD updated_at_month integer,
ADD updated_at_day integer;
-- you will have to find your own way to maintain these values...
UPDATE mytable
SET
inserted_at_month = date_part('month', inserted_at),
inserted_at_day = date_part('day', inserted_at),
updated_at_month = date_part('month', updated_at),
updated_at_day = date_part('day', updated_at);
如果用户仅输入年份,请使用 WHERE Date BETWEEN 'YYYY-01-01' AND 'YYYY-12-31'
SELECT *
FROM mytable
WHERE
inserted_at BETWEEN '2010-01-01' AND '2010-12-31'
OR updated_at BETWEEN '2010-01-01' AND '2010-12-31';
如果用户输入年份和月份,请使用 WHERE Date BETWEEN 'YYYY-MM-01' AND 'YYYY-MM-31'(可能需要调整 30/29/28)
SELECT *
FROM mytable
WHERE
inserted_at BETWEEN '2010-01-01' AND '2010-01-31'
OR updated_at BETWEEN '2010-01-01' AND '2010-01-31';
如果用户输入这三个值,请使用 SELECT .... WHERE Date = 'YYYY-MM-DD'
SELECT *
FROM mytable
WHERE
inserted_at = '2009-11-12'
OR updated_at = '2009-11-12';
如果用户输入月和日
SELECT *
FROM mytable
WHERE
inserted_at_month = 3
OR inserted_at_day = 31
OR updated_at_month = 3
OR updated_at_day = 31;
如果用户输入月或日(您可以优化为不检查值 > 12 作为一个月)
SELECT *
FROM mytable
WHERE
inserted_at_month = 12
OR inserted_at_day = 12
OR updated_at_month = 12
OR updated_at_day = 12;
关于ruby-on-rails - 是否可以以与数据库无关的方式将日期搜索为字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2175844/