9

Symfony2 フレームワークと Doctrine ORM を使用してアプリを構築しています。一部の IATA コードが欠落している航空会社の表があります。この IATA コードで並べ替えられたリストを出力していますが、IATA コードが null のレコードが一番上にソートされるという望ましくない結果が得られます。

MySQL では、これを行うのは簡単ORDER BY ISNULL(code_iata), code_iataですが、DQL と同等のものが何であるかについてはわかりません。私は試した

$er->createQueryBuilder('airline')->orderBy('ISNULL(airline.codeIata), airline.codeIata', 'ASC')

しかし、これにより構文エラーが発生します。

Doctrine のドキュメントにも答えはありません。方法はありますか?

4

6 に答える 6

18

DQL で次のトリックを使用して、NULL 値を最後に並べることができます。

$em->createQuery("SELECT c, -c.weight AS HIDDEN inverseWeight FROM Entity\Car c ORDER BY inverseWeight DESC");

HIDDENキーワード (Doctrine 2.2 以降で使用可能) は、結果セットからフィールドを省略し、望ましくない混合結果inverseWeightを防ぎます。

(並べ替えフィールドの値が逆になるため、順序も逆にする必要があります。そのため、クエリではDESC順序ではなく順序が使用されますASC。)

クレジットはこの回答に属します。

于 2014-04-11T08:44:30.277 に答える
14

最も目立たない一般的な解決策は、式をキーワードCASEと組み合わせて使用​​することです。HIDDEN

   SELECT e,
     CASE WHEN e.field IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
     FROM FooBundle:Entity e
 ORDER BY _isFieldNull ASC

数値と他のフィールド タイプの両方で動作し、Doctrine を拡張する必要はありません。

于 2016-02-03T17:36:22.187 に答える
7

これは、カスタム ウォーカーが必要なものを正確に取得する例です。githubの問題でDoctrineから取得しました:

https://github.com/doctrine/doctrine2/pull/100

しかし、そこにあるコードはMySQLでは機能しませんでした。MySQL で動作するように変更しましたが、他のエンジンについてはまったくテストしていません。

たとえば、次の walker クラスをYourNS\Doctrine\Waler\ディレクトリに配置します。

<?php

namespace YourNS\Doctrine\Walker;

use Doctrine\ORM\Query\SqlWalker;

class SortableNullsWalker extends SqlWalker
{
   const NULLS_FIRST = 'NULLS FIRST';
   const NULLS_LAST = 'NULLS LAST';

   public function walkOrderByClause($orderByClause)
   {
      $sql = parent::walkOrderByClause($orderByClause);

      if ($nullFields = $this->getQuery()->getHint('SortableNullsWalker.fields'))
      {
         if (is_array($nullFields))
         {
            $platform = $this->getConnection()->getDatabasePlatform()->getName();
            switch ($platform)
            {
            case 'mysql':
               // for mysql the nulls last is represented with - before the field name
               foreach ($nullFields as $field => $sorting)
               {
                  /**
                   * NULLs are considered lower than any non-NULL value,
                   * except if a – (minus) character is added before
                   * the column name and ASC is changed to DESC, or DESC to ASC;
                   * this minus-before-column-name feature seems undocumented.
                   */
                  if ('NULLS LAST' === $sorting)
                  {
                     $sql = preg_replace_callback('/ORDER BY (.+)'.'('.$field.') (ASC|DESC)/i', function($matches) {
                        if ($matches[3] === 'ASC') {
                           $order = 'DESC';
                        } elseif ($matches[3] === 'DESC') {
                           $order = 'ASC';
                        }
                        return ('ORDER BY -'.$matches[1].$matches[2].' '.$order);
                     }, $sql);
                  }
               }
                  break;
            case 'oracle':
            case 'postgresql':
               foreach ($nullFields as $field => $sorting)
               {
                  $sql = preg_replace('/(\.' . $field . ') (ASC|DESC)?\s*/i', "$1 $2 " . $sorting, $sql);
               }
               break;
            default:
               // I don't know for other supported platforms.
               break;
               }
            }
         }

         return $sql;
   }
}

それで:

use YourNS\Doctrine\Walker\SortableNullsWalker;
use Doctrine\ORM\Query;

[...]

$qb = $em->getRepository('YourNS:YourEntity')->createQueryBuilder('e');
$qb
   ->orderBy('e.orderField')
   ;

$entities = $qb->getQuery()
  ->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER,  '\YourNS\Doctrine\Walker\SortableNullsWalker')
  ->setHint('SortableNullsWalker.fields', array(
     'sortOrder' => SortableNullsWalker::NULLS_LAST
  ))
  ->getResult();
于 2013-01-02T11:45:41.663 に答える
3

DQL には、プレーン SQL のすべての関数が含まれているわけではありません。幸いなことに、カスタム DQL メソッドを定義してこれを実現できます。

いくつかのリソース:

http://punkave.com/window/2012/07/24/for-the-php-crowd-adding-custom-functions-to-doctrine-2-dql

http://docs.doctrine-project.org/en/2.1/cookbook/dql-user-defined-functions.html

http://symfony.com/doc/2.0/cookbook/doctrine/custom_dql_functions.html

于 2012-09-29T16:53:25.323 に答える
0

デフォルトでは、MySQL は引き続きNULL値をソートします。ソートされている場合は結果セットの先頭に配置され、ソートされASCている場合は最後に配置されDESCます。ここでは、 sort を探していますが、値を一番下に配置しASCたいと考えています。NULL

SELECT残念ながら、Doctrine は非常に強力ですが、関数のサポートが制限されており、そのほとんどが、WHERE、およびHAVING節に限定されているため、ここではあまりサポートを提供しません。QueryBuilder について次のいずれかが当てはまる場合、実際にはまったく問題はありません。

  • select()受け入れたISNULL()
  • orderBy()またはaddOrderBy()サポートISNULL()
  • クラスはUNIONs の概念をサポートしていました (これにより、2 つのクエリを実行できました。1 つは がcodeIataあった場所NULL、もう 1 つはそうでない場所で、それぞれを個別に並べ替えることができました)。

そうは言っても、ArtWorkAD が既に述べたユーザー定義関数を使用することも、2 つの異なる Doctrine クエリを使用して最後のポイントを複製することもできます。

$airlinesWithCode = $er->createQueryBuilder("airline")
    ->where("airline.iataCode IS NULL")
    ->getQuery()
    ->getResult();
$airlinesWithoutCode = $er->createQueryBuilder("airline")
    ->where("airline.iataCode IS NOT NULL")
    ->getQuery()
    ->getResult();

次に、これらを 1 つの配列に結合するか、テンプレートで個別に扱うことができます。

もう 1 つのアイデアは、DQL がすべてを 1 つのデータ セットで返し、PHP が面倒な作業を行うようにすることです。何かのようなもの:

$airlines = $er->findAll();
$sortedAirlines = array();
// Add non-NULL values to the end if the sorted array
foreach ($airlines as $airline)
    if ($airline->getCodeIata())
        $sortedAirlines[] = $airline;
// Add NULL values to the end of the sorted array
foreach ($airlines as $airline)
    if (!$airline->getCodeIata())
        $sortedAirlines[] = $airline;

これらの両方の欠点は、MySQL で s を実行できないことですLIMIT。そのため、比較的小さなデータ セットに対してのみうまく機能する可能性があります。

とにかく、これでうまくいくことを願っています!

于 2012-09-29T17:34:17.803 に答える