使用教义对varchar列进行自然排序的方式

问题描述 投票:-1回答:2

我有一个表,该表的列包含名称(varchar),但有些名称内部包含数字,因此不希望使用其顺序。

我有类似的东西:

Courlis
D11 Espadon
D13 Balbuzard
D1 empacher
D2

但是我希望:

Courlis
D1 empacher
D2
D11 Espadon
D13 Balbuzard

我已经找到了很多关于它的技巧,但是总是在仅将数字存储为字符串的情况下进行排序:添加0以将其转换为数字,检查字符串的长度以将其长度放在10之前的1,依此类推...但是我的情况不起作用。

我无法使用SQL查询,因为我以需要QueryBuilder的Symfony应用程序的形式使用它。

postgresql symfony doctrine doctrine-query natural-sort
2个回答
2
投票

这是使用PostgreSQL中的ICU归类(从v10开始提供)的一种方法:

CREATE COLLATION en_natural (
   LOCALE = 'en-US-u-kn-true',
   PROVIDER = 'icu'
);

CREATE TABLE atable (textcol text COLLATE en_natural);

COPY atable FROM STDIN;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself, or an EOF signal.
>> Courlis
>> D11 Espadon
>> D13 Balbuzard
>> D1 empacher
>> D2
>> \.

test=# SELECT textcol FROM atable ORDER BY textcol;

    textcol    
---------------
 Courlis
 D1 empacher
 D2
 D11 Espadon
 D13 Balbuzard
(5 rows)

0
投票

感谢Laurentz Albe为您在Symfony应用程序中的分步解答:

  1. 创建用于创建自定义归类的迁移文件
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
final class Version20200221215657 extends AbstractMigration
{
    public function getDescription() : string
    {
        return '';
    }

    public function up(Schema $schema) : void
    {
        // this up() migration is auto-generated, please modify it to your needs
        $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');

        $this->addSql('CREATE COLLATION fr_natural (provider = "icu", locale = "fr-u-kn-true");');
    }

    public function down(Schema $schema) : void
    {
        // this down() migration is auto-generated, please modify it to your needs
        $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');

        $this->addSql('DROP COLLATION fr_natural');
    }
}
  1. 创建主义自定义函数(Collat​​e DQL)
<?php

namespace App\DQL;

use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;

class Collate extends FunctionNode
{
    public $expressionToCollate = null;
    public $collation = null;

    public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->expressionToCollate = $parser->StringPrimary();

        $parser->match(Lexer::T_COMMA);
        $parser->match(Lexer::T_IDENTIFIER);

        $lexer = $parser->getLexer();

        $this->collation = $lexer->token['value'];

        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
    {
        return sprintf( '%s COLLATE %s', $sqlWalker->walkStringPrimary($this->expressionToCollate), $this->collation );
    }
}
  1. 在Doctrine配置中注册新功能
doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'

        # IMPORTANT: You MUST configure your server version,
        # either here or in the DATABASE_URL env var (see .env file)
        server_version: '12'
    orm:
        auto_generate_proxy_classes: true
        naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
        auto_mapping: true
        mappings:
            App:
                is_bundle: false
                type: annotation
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App
        dql:
            string_functions:
                collate: App\DQL\Collate

  1. 最后在需要时在查询中使用它
$query = $this->createQueryBuilder('shell')
    ->orderBy('COLLATE(shell.name, fr_natural)', 'ASC')
    ->getQuery();

return $query->getResult();
© www.soinside.com 2019 - 2024. All rights reserved.