我只是摆弄了使用DBIx::Class
生成的复杂SQL查询(对于WHERE子句,它使用了SQL::Abstract
。)
我正在使用BETWEEN运算符来过滤条目:
# SELECT title FROM tracks WHERE begin BETWEEN '2016-07-01' AND '2016-07-02'
$searchArgs->{'begin'} = { BETWEEN => ['2016-07-01', '2016-07-02' ] };
这将在2016-07-02实际上跳过开始日期。可能是因为这些开始值已经过了2016-07-02的午夜(00:00),因此不在BETWEEN范围内(参见最后的示例数据)。
在SQL端,这很容易解决:
SELECT title FROM tracks WHERE DATE(begin) BETWEEN '2016-07-01' AND '2016-07-02'
# Note the DATE()-cast of begin
有没有办法用SQL::Abstract
语法实现这一目标?我尝试将列名称作为标量引用($searchArgs->{\"DATE(begin)"}
)以使其不受影响地传递,但它没有用完。
仅使用$searchArgs->{'DATE(begin)'}
会触发SQL-Error,因为没有这样的列“DATE(begin)”。
# Minimal example data
CREATE TABLE test ( title VARCHAR(255), begin timestamp );
INSERT INTO test (title, begin) VALUES ( 'Track 1', '2016-07-01T12:00:00'::timestamp), ( 'Track 2', '2016-07-02 12:00:00'::timestamp );
SELECT title FROM test WHERE begin BETWEEN '2016-07-01' AND '2016-07-02';
SELECT title FROM test WHERE DATE(begin) BETWEEN '2016-07-01' AND '2016-07-02';
答案 0 :(得分:4)
问题似乎是'2016-07-02'
之类的日期等同于'2016-07-02T00:00:00'
,而'2016-07-02T12:00:00'
大于。调用DATE()
只会截断时间值,以便丢弃额外的12小时
我建议您放弃BETWEEN
,并使用等效的>=
和<=
运算符。您实际上希望所有记录最多但不包括 '2016-07-03T0:00:00'
(即,包括2016-07-02T23:59:59
在内的所有内容以及您的数据库可能支持的任何分数)所以您应该写
SELECT title FROM test
WHERE begin BETWEEN >= '2016-07-01' AND begin < '2016-07-03';
或,在DBIx::Class
$searchArgs->{begin} = {
'>=' => '2016-07-01',
'<' => '2016-07-03',
};
或者如果您只能访问结束日期并希望避免在Perl代码中使用日期算术,那么只需让数据库为您执行此操作
$searchArgs->{begin} = {
'>=' => '2016-07-01',
'<' => \[ 'date(?) + 1', '2016-07-02' ],
};
如果你坚持在WHERE比较中使用函数而不是简单的表名,那么可以使用文字SQL,但它是不可取的,因为你将绕过数据库可以提供的所有优化,它将不得不诉诸线性搜索
DBIx::Class::Manual::Cookbook
文档在Using SQL functions on the left hand side of a comparison
在比较的左侧使用SQL函数通常不是一个好主意,因为它需要扫描整个表。 (除非您的RDBMS支持表达式的索引 - 包括函数的返回值 - 并且您在相关函数的返回值上创建索引。)但是,必要时可以使用
DBIx::Class
通过使用文字SQL来完成