2016-11-07 8 views
1

これはちょっと変わったことです:以下のMySQL Stored Proc(または関数)があります。完全な画像を理解する必要がない限り、大多数は無視できます。問題は、ORDER BY句です。ORDER BY句のMySQL IFステートメント

BEGIN 

    SELECT DISTINCT e.*, (3959 * acos(cos(radians(in_latitude)) * cos(radians(e.address_latitude)) 
     * cos(radians(e.address_longitude) - radians(in_longitude)) + sin(radians(in_latitude)) 
     * sin(radians(e.address_latitude)))) AS distanceFromUsersPostcode 
    FROM event e 
    INNER JOIN event_organiser eo on e.event_organiser_id = eo.id 
    WHERE (e.event_name LIKE in_search OR e.address_town LIKE in_search OR e.address_county LIKE in_search OR eo.event_organiser_name LIKE in_search) 
    AND e.start_date_time >= in_start_date 
    AND e.start_date_time <= in_end_date 
    AND e.enabled = true 
    HAVING distanceFromUsersPostcode < in_maxDistanceFromUser 
    /* 
     * 1 * 
     ORDER BY distanceFromUsersPostcode 
     * 2 * 
     ORDER BY IF(in_orderBy='LOCATION', CAST(distanceFromUsersPostcode AS DECIMAL), e.start_date_time) ASC; 
    */ 
    ORDER BY 
     CASE in_orderBy 
      WHEN 'LOCATION' THEN distanceFromUsersPostcode 
      ELSE e.start_date_time 
     END 
    ASC; 

END 

さて問題はこれで、ORDER BY句現在のコメントを外しORDERはVARCHAR(または列)の値としてDECIMAL値distanceFromUsersPostcodeを治療するためのようです。

それは形式で結果順序付け:

0.4、101.9、102.8、11.1、11.9

Iしかし* 2 *

として標識された変異体を使用した場合ならば同じことが、言うことができるが私は* 1 *として示さ元バリアントに戻す、結果は予想通り注文することになります。

0.4、11.1、11.9、101.9、102.8

私の推測をIF関数(* 2 *)内で使用されている場合、MySQLはdistanceFromUsersPostcode変数をVARCHARとして扱いました。したがって、これをDECIMALにキャストしようとしました。しかし、これは効果がありません。

ここで何が起こっているのか誰にも分かりますか?

予想通り、以下の行為が、それはクエリ全体を複製として当然の非常にエレガントではありません。

BEGIN 
    IF in_orderBy='LOCATION' THEN 

     SELECT DISTINCT e.*, (3959 * acos(cos(radians(in_latitude)) * cos(radians(e.address_latitude)) 
      * cos(radians(e.address_longitude) - radians(in_longitude)) + sin(radians(in_latitude)) 
      * sin(radians(e.address_latitude)))) AS distanceFromUsersPostcode 
     FROM event e 
     INNER JOIN event_organiser eo on e.event_organiser_id = eo.id 
     WHERE (e.event_name LIKE in_search OR e.address_town LIKE in_search OR e.address_county LIKE in_search OR eo.event_organiser_name LIKE in_search) 
     AND e.start_date_time >= in_start_date 
     AND e.start_date_time <= in_end_date 
     AND e.enabled = true 
     HAVING distanceFromUsersPostcode < in_maxDistanceFromUser 
     ORDER BY distanceFromUsersPostcode; 

    ELSE 

     SELECT DISTINCT e.*, (3959 * acos(cos(radians(in_latitude)) * cos(radians(e.address_latitude)) 
      * cos(radians(e.address_longitude) - radians(in_longitude)) + sin(radians(in_latitude)) 
      * sin(radians(e.address_latitude)))) AS distanceFromUsersPostcode 
     FROM event e 
     INNER JOIN event_organiser eo on e.event_organiser_id = eo.id 
     WHERE (e.event_name LIKE in_search OR e.address_town LIKE in_search OR e.address_county LIKE in_search OR eo.event_organiser_name LIKE in_search) 
     AND e.start_date_time >= in_start_date 
     AND e.start_date_time <= in_end_date 
     AND e.enabled = true 
     HAVING distanceFromUsersPostcode < in_maxDistanceFromUser 
     ORDER BY e.start_date_time; 

    END IF; 
END 

答えて

0

MySQLをソートするための異なるデータ型を使用しているためであることを便利なものに変換しようとします値を比較することができます。あなたの特定のケースでは、それは文字列への変換になります。したがって、distanceFromUsersPostcodeを数値に変換するのは意味がありません。これは、文字列に変換されるためです。数値ソートに便利な形式で文字列を変換する必要があります。 LPAD関数がここに役立ちます。

ORDER BY 
    CASE in_orderBy 
     WHEN 'LOCATION' THEN LPAD(CAST(distanceFromUsersPostcode as CHAR),6) 
     ELSE e.start_date_time 
    END 
ASC; 
+0

ご意見ありがとうございます。あなたが示唆したようにコンパイルされず、LPAD(CAST(distanceFromUsersPostcode as CHAR)、6、 '0')を左パッドに0に変更しました。しかし、残念ながら、それは以前と同じ問題ではありませんでした。 –

+0

しかし、これはMySQLがレキシカルソートを適用しているという事実を考えています(2つのタイプが一致します)。私は、WHEN 'LOCATION' THEN(distanceFromUsersPostcode + 10000)を使用してクエリを動作させることができました。これで問題は解決しましたが、大きな懸念につながります。 MySQLは現在、どのソートが提供されているかに応じて、数値(または日付)でソートするのではなく、レキシカルソートを実行しています。私は他の場所でこれを読むと、クエリの実行がずっと遅くなる可能性があります。これが当てはまる場合、最初に表示されたコードセグメントを複製するのは幸いです。あまりエレガントではありませんが、より高速なクエリ何かご意見は? –

+0

あなたはそれが働いてうれしいです。このソート要件は珍しいことです。別の方法であなたの要求を考えるのではないでしょうか? –