连接字符串并将其追加到PostgreSQL中的where子句时出现语法错误,如何修复?

jvidinwx  于 2023-05-28  发布在  PostgreSQL
关注(0)|答案(2)|浏览(228)

我试图创建一个动态字符串并将其附加到postgresql拜特中的where子句中,我总是得到一个错误声明;

ERROR:  syntax error at or near "4"
LINE 13:      when 4 = ANY(stars) then appendtext := appendtext || ' ...
                   ^ 

SQL state: 42601
Character: 332

这是函数

CREATE OR REPLACE FUNCTION public.reviewsbystartotal(
    cid integer, stars integer[])
    RETURNS integer
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE PARALLEL UNSAFE

AS $BODY$
declare appendtext text := '';
BEGIN       
         case when 5 = ANY(stars) then appendtext := appendtext || ' and cr.generalrating >= 4.25'
              when 4 = ANY(stars) then appendtext := appendtext || ' and cr.generalrating >= 3.25 and cr.generalrating <= 4.24'
              when 3 = ANY(stars) then appendtext := appendtext || ' and cr.generalrating >= 2.5 and cr.generalrating <= 3.24'
              when 2 = ANY(stars) then appendtext := appendtext || ' and cr.generalrating >= 1.75 and cr.generalrating <= 2.49'
              when 1 = ANY(stars) then appendtext := appendtext || ' and cr.generalrating >= 1 and cr.generalrating <= 1.74'
         else

         RETURN (Select count(1) from companyreviews cr where cr.company=cid 
                 and cr.internalapproval=true
         || appendtext);
         
         

END; 
$BODY$;

我试着在postgresql中concanate一个字符串并附加它的where子句。

pxiryf3j

pxiryf3j1#

串联运算符||应放在SELECT语句的括号外。
下面是你的函数的修正版本:

CREATE OR REPLACE FUNCTION public.reviewsbystartotal(
    cid integer, stars integer[])
    RETURNS integer
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE PARALLEL UNSAFE

AS $BODY$
DECLARE
    appendtext text := '';
BEGIN       
    CASE
        WHEN 5 = ANY(stars) THEN
            appendtext := appendtext || ' and cr.generalrating >= 4.25';
        WHEN 4 = ANY(stars) THEN
            appendtext := appendtext || ' and cr.generalrating >= 3.25 and cr.generalrating <= 4.24';
        WHEN 3 = ANY(stars) THEN
            appendtext := appendtext || ' and cr.generalrating >= 2.5 and cr.generalrating <= 3.24';
        WHEN 2 = ANY(stars) THEN
            appendtext := appendtext || ' and cr.generalrating >= 1.75 and cr.generalrating <= 2.49';
        WHEN 1 = ANY(stars) THEN
            appendtext := appendtext || ' and cr.generalrating >= 1 and cr.generalrating <= 1.74';
    END CASE;

    RETURN (
        SELECT COUNT(1)
        FROM companyreviews cr
        WHERE cr.company = cid
        AND cr.internalapproval = true
    ) || appendtext;

END;
$BODY$;
6jjcrrmo

6jjcrrmo2#

OP似乎打算动态构建并执行查询,以返回公司在多个离散范围内的评级数量。如果未选择任何范围,则查询应返回公司的评等总数。OP的代码将范围定义为闭合区间。我将它们实现为半开间隔,以防止范围之间的潜在差距。
以下代码将该功能实现为PL/pgSQL函数:

CREATE OR REPLACE FUNCTION public.reviewsbystartotal(
  cid integer, stars integer[])
  RETURNS integer
  LANGUAGE plpgsql
  VOLATILE PARALLEL SAFE
AS
$BODY$
DECLARE
  appendtext  text := '';
  num_matched integer;
BEGIN
  IF 5 = ANY (stars) THEN
    appendtext := appendtext || ' and cr.generalrating >= 4.25';
  END IF;
  IF 4 = ANY (stars) THEN
    appendtext := appendtext || ' and cr.generalrating >= 3.25 and cr.generalrating < 4.25';
  END IF;
  IF 3 = ANY (stars) THEN
    appendtext := appendtext || ' and cr.generalrating >= 2.5 and cr.generalrating < 3.25';
  END IF;
  IF 2 = ANY (stars) THEN
    appendtext := appendtext || ' and cr.generalrating >= 1.75 and cr.generalrating < 2.5';
  END IF;
  IF 1 = ANY (stars) THEN
    appendtext := appendtext || ' and cr.generalrating >= 1 and cr.generalrating < 1.75';
  END IF;

  EXECUTE FORMAT($$SELECT COUNT(1) FROM companyreviews cr WHERE cr.company = $1 AND cr.internalapproval = TRUE %s$$,
                 appendtext)
    INTO num_matched
    USING cid;

  RETURN num_matched;
END
$BODY$;

所需的行为可以实现为SQL函数,而无需采用动态SQL,如下所示:

CREATE OR REPLACE FUNCTION public.reviewsbystartotal(
  cid integer, stars integer[])
  RETURNS integer
  LANGUAGE SQL
  VOLATILE PARALLEL SAFE
AS
$BODY$
WITH ratings_ranges(star_level, ratings_range) AS (VALUES (1, NUMRANGE(1, 1.75)),
                                                          (2, NUMRANGE(1.75, 2.5)),
                                                          (3, NUMRANGE(2.5, 3.25)),
                                                          (4, NUMRANGE(3.25, 4.25)),
                                                          (5, NUMRANGE(4.25, NULL))),
     selected_ranges AS (SELECT ratings_ranges.ratings_range
                           FROM ratings_ranges
                           WHERE reviewsbystartotal.stars IS NULL
                              OR ARRAY_LENGTH(reviewsbystartotal.stars, 1) IS NULL
                              OR ratings_ranges.star_level = ANY (reviewsbystartotal.stars))
SELECT COUNT(*)
  FROM companyreviews cr
    JOIN selected_ranges sr
         ON cr.generalrating <@ sr.ratings_range
  WHERE cr.company = reviewsbystartotal.cid
    AND cr.internalapproval;
$BODY$;

所有列和变量引用都经过限定,以防止潜在的标识符二义性。
PL/pgSQL和SQL实现之间存在一个已知的行为差异:如果stars数组中的所有项目的值都超出了有效范围1-5,则PL/pgSQL版本将返回该公司的所有评级的计数,而SQL版本将返回计数0。

相关问题