DBIx::Class / SQL::Abstract 对 WHERE 子句中的列应用 SQL-函数
DBIx::Class / SQL::Abstract Apply SQL-Function to column in WHERE clause
我只是摆弄了一个复杂的 SQL 查询,该查询是使用 DBIx::Class
(which under the hood uses SQL::Abstract
为 WHERE 子句生成的。
我正在使用 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-错误,因为没有这样的列 "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';
问题似乎是像 '2016-07-02'
这样的日期相当于 '2016-07-02T00:00:00'
,而 '2016-07-02T12:00:00'
大于 。调用 DATE()
只是截断时间值,以便丢弃额外的十二小时
我建议您放弃 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 比较中使用函数而不是简单的 table 名称,则可以使用文字 SQL,但不建议这样做,因为您将绕过所有优化数据库可以提供,它将不得不求助于线性搜索
DBIx::Class::Manual::Cookbook
文档在 Using SQL functions on the left hand side of a comparison
下有这个
Using SQL functions on the left hand side of a comparison is generally not a good idea since it requires a scan of the entire table. (Unless your RDBMS supports indexes on expressions - including return values of functions - and you create an index on the return value of the function in question.) However, it can be accomplished with DBIx::Class
when necessary by resorting to literal SQL
我只是摆弄了一个复杂的 SQL 查询,该查询是使用 DBIx::Class
(which under the hood uses SQL::Abstract
为 WHERE 子句生成的。
我正在使用 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-错误,因为没有这样的列 "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';
问题似乎是像 '2016-07-02'
这样的日期相当于 '2016-07-02T00:00:00'
,而 '2016-07-02T12:00:00'
大于 。调用 DATE()
只是截断时间值,以便丢弃额外的十二小时
我建议您放弃 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 比较中使用函数而不是简单的 table 名称,则可以使用文字 SQL,但不建议这样做,因为您将绕过所有优化数据库可以提供,它将不得不求助于线性搜索
DBIx::Class::Manual::Cookbook
文档在 Using SQL functions on the left hand side of a comparison
Using SQL functions on the left hand side of a comparison is generally not a good idea since it requires a scan of the entire table. (Unless your RDBMS supports indexes on expressions - including return values of functions - and you create an index on the return value of the function in question.) However, it can be accomplished with
DBIx::Class
when necessary by resorting to literal SQL