如何使用 SQLite 使 DBIx::Class 忽略 ORDER BY 中的大小写?

How do I make DBIx::Class ignore case in ORDER BY with SQLite?

通常 SQLite 的排序规则区分大小写。所有大写字母都在小写字母之前。但是可以在 ORDER BY 子句中告诉 SQLite 忽略它,by doing this:

... ORDER BY foo COLLATE NOCASE ASC

但是我们如何用 DBIx::Class 做到这一点?

考虑以下示例,它在内存中部署了一个 SQLite 数据库,其中包含 table foo 和一个 bar comlumn。连接使用the quote_names setting. It fills in the values z Z b B a A and then gets them back out using all on the ResultSet. I'll be using this setup in all my following examples. You need DBIx::Class and 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;

此输出区分大小写。

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,但没有 运行ning 任何 SQL。我不想在 ORDER BY 中进行任何昂贵的字符串转换,或稍后在 Perl 中进行。

如何在使用 SQLite 订购时告诉 DBIx::Class 使用 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 Exception: DBD::SQLite::db prepare_cached failed: no such column: me.bar COLLATE NOCASE [for Statement "SELECT "me"."bar" FROM "foo" "me" ORDER BY "me"."bar COLLATE NOCASE" ASC"]

或者我可以通过使用 DBIC 在 ORDER BY 子句中转换为 upperlower 来实现。

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

without quote_names 这很相似,但相反。 (这不是我关心的问题),但在打开 quote_names 时也会抛出错误。

SELECT "me"."bar" FROM "foo" "me" ORDER BY "lower(me"."bar)" ASC: DBIx::Class::Storage::DBI::_prepare_sth(): DBI Exception: DBD::SQLite::db prepare_cached failed: no such column: lower(me.bar) [for Statement "SELECT "me"."bar" FROM "foo" "me" ORDER BY "lower(me"."bar)" ASC"]

如果您愿意使用非常少量的 SQL,您可以传递一个标量引用来表示文字 SQL,DBIC 不会弄乱它:

order_by => \'me.bar COLLATE NOCASE ASC'

或者,仅使用最少量的文字 SQL:

order_by => { -asc => \'me.bar COLLATE NOCASE' }

请注意,此语法是 technically discouraged,但我不知道有任何其他方法可以实现您所追求的目标:

The old scalarref syntax (i.e. order_by => \'year DESC') is still supported, although you are strongly encouraged to use the hashref syntax as outlined above.