【发布时间】:2017-05-17 19:39:58
【问题描述】:
SQLite 的排序规则通常区分大小写。所有大写字母都在小写字母之前。但是可以在 ORDER BY 子句中告诉 SQLite 忽略它,by doing this:
... ORDER BY foo COLLATE NOCASE ASC
但是我们如何使用 DBIx::Class 做到这一点?
考虑以下示例,它在内存中部署了一个 SQLite 数据库,其中包含一个表 foo 和一个列 bar。连接使用the quote_names setting。它填充值z Z b B a A,然后在ResultSet 上使用all 将它们取回。我将在以下所有示例中使用此设置。你需要 DBIx::Class 和 DBD::SQLite 来运行它。
use strict;
use warnings;
package Foo::Schema::Result::Foo;
use base 'DBIx::Class::Core';
__PACKAGE__->table("foo");
__PACKAGE__->add_columns( "bar", { data_type => "text" }, );
package Foo::Schema;
use base 'DBIx::Class::Schema';
__PACKAGE__->register_class( 'Foo' => 'Foo::Schema::Result::Foo' );
package main;
my $schema = Foo::Schema->connect(
{
dsn => 'dbi:SQLite:dbname=:memory:',
quote_names => 1,
}
);
$schema->deploy;
$schema->resultset('Foo')->create( { bar => $_ } ) for qw(z Z b B a A);
my @all = $schema->resultset('Foo')->search(
{},
{
order_by => { -asc => 'me.bar' },
},
)->all;
# example code starts here
print join q{ }, map { $_->bar } @all;
this 的输出区分大小写。
A B Z a b z
现在我当然可以用 Perl 对其进行排序并使其不区分大小写,就像这样。
print join q{ }, sort { lc $a cmp lc $b } map { $_->bar } @all;
现在我明白了
A a B b Z z
但如果我直接使用底层 DBI 句柄进行查询,我也可以使用 COLLATE NOCASE。
$schema->storage->dbh_do(
sub {
my ( $storage, $dbh, @args ) = @_;
my $res = $dbh->selectall_arrayref(
"SELECT * FROM foo ORDER BY bar COLLATE NOCASE ASC"
);
print "$_->[0] " for @$res;
}
这给了我们
a A b B z Z
我希望 DBIC 使用COLLATE NOCASE,但不运行任何 SQL。我不想在ORDER BY 中进行任何昂贵的字符串转换,或者稍后在 Perl 中进行。
如何告诉 DBIx::Class 在使用 SQLite 订购时使用 COLLATE NOCASE?
以下不起作用:
order_by => { '-collate nocase asc' => 'me.bar' },
这只有在quote_names 没有打开时才有效。
order_by => { -asc => 'me.bar COLLATE NOCASE' },
它将使用上面的示例代码生成此查询和错误消息。
SELECT "me"."bar" FROM "foo" "me" ORDER BY "me"."bar COLLATE NOCASE" ASC: DBIx::Class::Storage::DBI::_prepare_sth(): DBI 异常: DBD::SQLite::db prepare_cached 失败:没有这样的列:me.bar COLLATE NOCASE [for Statement "SELECT "me"."bar" FROM "foo" "me" ORDER BY "我"."bar COLLATE NOCASE" ASC"]
或者我可以通过使用 DBIC 在 ORDER BY 子句中转换为 upper 或 lower 来实现。
my @all = $schema->resultset('Foo')->search(
{},
{
order_by => { -asc => 'lower(me.bar)' },
},
)->all;
print join q{ }, map { $_->bar } @all;
这给了
a A b B z Z
没有quote_names,这是相似的,但反过来。 (这不是我关心的问题),但在打开quote_names 时也会引发错误。
SELECT "me"."bar" FROM "foo" "me" ORDER BY "lower(me"."bar)" ASC: DBIx::Class::Storage::DBI::_prepare_sth():DBI 异常: DBD::SQLite::db prepare_cached 失败:没有这样的列:lower(me.bar) [for Statement "SELECT "me"."bar" FROM "foo" "me" ORDER BY "降低(我"."bar)" ASC"]
【问题讨论】:
-
AFAIK,唯一的方法是使用文字 SQL,这意味着传递一个标量引用:
order_by => \'me.bar COLLATE NOCASE ASC'。 FWIW,这也有效:order_by => { -asc => \'me.bar COLLATE NOCASE' }. -
你想写一个答案@Matt 吗?文字 SQL 很好,即使它把我们绑定到 SQLite。在这种情况下,我不在乎。
-
我犹豫是否要发布答案,因为您说您不想使用任何 SQL,并且因为我不确定(现在仍然不确定)是否有更好的方法来做到这一点.
-
@matt 看来你比我读得更好。你是对的,我说过,但我的意思是使用 dbh 的示例。 ;) 如果有更好的答案,我们会找到的。但这是一个好的开始。谢谢。
标签: perl sqlite dbix-class