防止评估不匹配的案例

时间:2016-07-28 14:08:39

标签: sql postgresql

我在select:

中有这个case语句
CASE ('$5' = '')
  WHEN NOT FALSE THEN TRUE
ELSE
  ST_DWithin(latlong_g, ST_SetSRID(ST_Point($5::float, $6::float), 4326), $7, false) END

因此,如果给予prepare的参数为空,则case的计算结果为true(对于封闭以及在查询中使用的位置),否则将执行else。

不幸的是,似乎ELSE由pg解析器进行评估,并且它会消息:

ERROR: invalid input syntax for type double precision: ""

有没有办法防止这种错误?

编辑:完整查询以便清晰:

SELECT addritems.*, 
       St_y(adresse.latlong), 
       St_x(adresse.latlong) 
FROM   adresse 
inner join addritems 
        ON addritems.adrcd = adresse.adrcd 
       AND addritems.plz LIKE Coalesce(Nullif($2, ''), addritems.plz) 
       AND CASE ( $3 = '' ) 
                 WHEN NOT FALSE THEN TRUE 
                 ELSE St_dwithin(latlong_g, 
                                 St_setsrid(St_point($3::FLOAT, $4:: FLOAT), 4326), 
                                 $5, FALSE)
           END 

2 个答案:

答案 0 :(得分:2)

如何使用 OR:

SELECT addritems.*, St_y(adresse.latlong), St_x(adresse.latlong) 
FROM  adresse 
inner join addritems 
  ON addritems.adrcd = adresse.adrcd 
  AND addritems.plz LIKE Coalesce(Nullif($2, ''), addritems.plz) 
  AND ( 
        $3 = '' 
        OR St_dwithin(latlong_g,St_setsrid(St_point($3::FLOAT, $4::FLOAT),4326),$5,FALSE)
      )

从评论中回答亚历克斯:

原因是:Postgresql 在解析查询时,将 ( 1/0) 视为一个常量,并尝试通过实际潜水数字使其更简单,而如果您将其替换为值不能为的值在解析步骤评估,它将推迟到执行时间。比如将(1/0)替换为(1/floor(random()),查询会成功运行,因为执行时会显示“random()”的值。

SELECT 
    current_catalog
    ,current_catalog::text = 'test'
    ,CASE
        WHEN current_catalog::text = 'test' THEN 1  -- division by zero
       ELSE 1 / floor(random())
    END 

注意: floor(random()) 始终为 0,因为 random() 介于 0 和 1 之间。

答案 1 :(得分:0)

我认为你应该像这样编写你的CASE

CASE 
    WHEN ('$5' = '') THEN NULL  -- or -1 some other constant, 
                                -- I guess distance cant be negative.
    ELSE ST_DWithin(latlong_g, 
                    ST_SetSRID(ST_Point('$5'::float, '$6'::float), 4326), 
                    $7, false) 
END

如果NULL适合您,也可以使用CASE

中的默认值
CASE 
    WHEN ('$5' <> '') 
    THEN ST_DWithin(latlong_g, 
                    ST_SetSRID(ST_Point('$5'::float, '$6'::float), 4326), 
                    $7, false) 
    -- NO NEED ELSE mean default to NULL.
END

编辑:查询后需要

   AND (( $3 = '')  OR
        ( St_dwithin(latlong_g, 
                     St_setsrid(St_point($3::FLOAT, $4::FLOAT), 4326), 
                     $5, FALSE)
        ))