2016-11-07 56 views
1

这有点奇怪:我有以下的MySQL存储过程(或函数),大多数可以安全地忽略,除非需要了解完整的图片。问题是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子句好像对待十进制值distanceFromUsersPostcode为VARCHAR(或字符串)值。

它订单的结果形式:

0.4,101.9,102.8,11.1,11.9

同样可以如果我用标记为* 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')用零填充到左边的pad。然而,不幸的是,它没有像预期的那样工作,就像以前一样。 –

+0

但是,这让我想到了MySQL正在应用词法排序(所以这两种类型匹配)的事实。我设法通过使用WHEN'LOCATION'THEN(distanceFromUsersPostcode + 10000)来使查询工作。这已经解决了这个问题,但让我更加关注。 MySQL现在正在执行词法排序,而不是按数字(或日期)进行排序,具体取决于提供的排序。我在别处读过这可能会导致查询运行速度慢得多。如果是这种情况,我会更快乐地复制原来显示的代码段。不太优雅但更快速的查询。有什么想法吗? –

+0

我很高兴你能工作。这种排序要求并不常见。你能不能以不同的方式思考你的要求? –