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

时间:2017-01-03 16:37:25

标签: perl sqlite dbix-class

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

... ORDER BY foo COLLATE NOCASE ASC

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

考虑以下示例,该示例使用表foo和一个comlumn bar在内存中部署SQLite数据库。该连接使用the quote_names setting。它填充值z Z b B a A,然后使用ResultSet上的all将它们取回。我将在以下所有示例中使用此设置。您需要DBIx::ClassDBD::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,但不运行任何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异常:   DBD :: SQLite :: db prepare_cached失败:没有这样的列:me.bar COLLATE   NOCASE [for Statement“SELECT”me“。”bar“FROM”foo“”me“ORDER BY   “我”。“吧COLLATE NOCASE”ASC“]

或者我可以使用DBIC转换为upper子句中的lowerORDER BY

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   “lower(me”。“bar”“ASC”]

1 个答案:

答案 0 :(得分:4)

如果你习惯使用极少量的SQL,你可以传递一个标量引用来表示文字SQL,而DBIC不会惹它:

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

或者,只有最少量的文字SQL:

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

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

  

旧的scalarref语法(即order_by => \'年DESC')仍然是   支持,但强烈建议您使用hashref   语法如上所述。