用于ORDER BY子句的MySQL IF语句问题

时间:2016-11-07 15:22:15

标签: mysql

这有点奇怪:我有以下MySQL存储过程(或函数),除非需要了解全貌,否则大多数都可以安全地忽略。问题是ORDER BY子句。

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子句似乎将DECIMAL值distanceFromUsersPostcode视为VARCHAR(或字符串)值。

它以以下形式订购结果:

0.4,101.9,10.2.8,11.1,11.9

如果我使用标记为* 2 *

的变体,也可以这样说

但是,如果我恢复标记为* 1 *的原始变体,结果将按预期排序:

0.4,11.1,11.9,101.9,102.8

我的猜测是,如果在IF函数(* 2 *)中使用,则将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

1 个答案:

答案 0 :(得分: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;