【问题标题】:How do I make DBIx::Class ignore case in ORDER BY with SQLite?如何使用 SQLite 使 DBIx::Class 忽略 ORDER BY 中的大小写?
【发布时间】: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::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;

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 子句中转换为 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

没有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


【解决方案1】:

如果您习惯使用非常少量的 SQL,您可以传递一个标量引用来表示文字 SQL,DBIC 不会搞砸它:

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

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

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

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

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

【讨论】:

  • 值得注意的是,不鼓励使用 scalarref 语法 order_by => \SCALAR,但这意味着 scalarref-in-a-hashref 语法 order_by => { -asc => \SCALAR } 仍然完美无缺
猜你喜欢
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
  • 2012-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-26
相关资源
最近更新 更多