【问题标题】:Query string argument of EXECUTE is nullEXECUTE 的查询字符串参数为空
【发布时间】:2016-06-29 22:06:51
【问题描述】:

编辑

看来我的问题是当这个 select 语句返回 null 时(这是我试图处理的情况 - 当它返回 null 时,我希望我的新值是 -999)。如果发现空值时出错,我该怎么做?

原创

我已经阅读了我能找到的关于此错误的所有其他 SO 帖子,但似乎都没有解决我的问题的根源。

错误非常简单——我的 EXECUTE 语句中的一个参数为空。伟大的。但是,我在 EXECUTE 语句被调用之前打印出构成 EXECUTE 语句的每个值,并且我可以清楚地看到没有一个值是 null。

代码:

CREATE FUNCTION inform_icrm_prob_flow_query(tablename text, location_id int,
                                            product_date_str text, lead_time_start int,
                                            lead_time_end int, first_member_id int,
                                            last_member_id int, dest_file text)
RETURNS void AS $$
DECLARE
  count int;
  product_date TIMESTAMPTZ;
  interval_lead_time_start text;
  interval_lead_time_end text;
  curr_value double precision;
  query text;
BEGIN
  product_date := product_date_str::TIMESTAMPTZ;
  count := first_member_id;
  curr_value := 0;

  interval_lead_time_start :=  ''''|| product_date ||'''::timestamptz +
                               interval '''||lead_time_start||' hours''';
  interval_lead_time_end :=  ''''|| product_date ||'''::timestamptz +
                             interval '''||lead_time_end||' hours'' -
                             interval ''6 hours''';

  --create our temporary table and populate it's date column
  EXECUTE 'CREATE TEMPORARY TABLE temp_table_icrm_prob_flow AS 
             SELECT * FROM generate_series('||interval_lead_time_start || ',' || 
                                            interval_lead_time_end || ', ''6 hours'') 
                           AS date_valid';
  LOOP
    EXIT WHEN count > last_member_id;
    IF NOT EXISTS(
      SELECT 'date_valid'
      FROM information_schema.columns
      WHERE table_name='temp_table_icrm_prob_flow'
        and column_name='value'||count||'') 
    THEN
      EXECUTE 'ALTER TABLE temp_table_icrm_prob_flow ADD COLUMN value' || count 
               || ' double precision DEFAULT -999';
    END IF;

    raise notice 'tablename: %', tablename;
    raise notice 'location_id: %', location_id;
    raise notice 'product_date: %', product_date;
    raise notice 'count: %', count;

    query := 'SELECT value FROM '|| tablename ||' 
              INNER JOIN temp_table_icrm_prob_flow
              ON (temp_table_icrm_prob_flow.date_valid = '|| tablename ||'.date_valid)
              WHERE '|| tablename ||'.id_location = '|| location_id ||'
                AND '|| tablename ||'.date_product = '''|| product_date ||'''
                AND '|| tablename ||'.id_member = '|| count ||'';

    EXECUTE query INTO curr_value;

    EXECUTE 'UPDATE temp_table_icrm_prob_flow 
             SET value'|| count ||' = COALESCE('|| curr_value ||', -999)';

    count := count + 1;
  END LOOP;

  EXECUTE 'ALTER TABLE temp_table_icrm_prob_flow DROP COLUMN date_valid';

  EXECUTE 'COPY temp_table_icrm_prob_flow TO '''||dest_file||''' DELIMITER '','' CSV';

  EXECUTE 'DROP TABLE temp_table_icrm_prob_flow';
END;
$$ LANGUAGE plpgsql;

输出:

NOTICE:  tablename: inform_tseries_data_basin_proc_fcst_prob_flow
NOTICE:  location_id: 38
NOTICE:  product_date: 2015-02-05 12:00:00+00
NOTICE:  count: 1
ERROR:  query string argument of EXECUTE is null
CONTEXT:  PL/pgSQL function inform_icrm_prob_flow_query(text,integer,text,integer,integer,integer,integer,text) line 38 at EXECUTE

如果我传入的变量都不是 null,并且唯一引用的其他东西是我知道存在的临时表,那么可能导致此错误的原因是什么?

注意:将我的查询更改为:

query := 'SELECT value FROM '|| tablename ||' WHERE '|| tablename ||'.id_location = '|| location_id ||' AND '|| tablename ||'.date_product = '''|| product_date ||''' AND '|| tablename ||'.id_member = '|| count ||' AND temp_table_icrm_prob_flow.date_va    lid = '|| tablename ||'.date_valid';

我收到以下错误:

NOTICE:  tablename: inform_tseries_data_basin_proc_fcst_prob_flow
NOTICE:  location_id: 38
NOTICE:  product_date: 2015-02-05 12:00:00+00
NOTICE:  count: 1
ERROR:  missing FROM-clause entry for table "temp_table_icrm_prob_flow"
LINE 1: ..._data_basin_proc_fcst_prob_flow.id_member = 1 AND temp_table...
                                                         ^
QUERY:  SELECT value FROM inform_tseries_data_basin_proc_fcst_prob_flow WHERE inform_tseries_data_basin_proc_fcst_prob_flow.id_location = 38 AND inform_tseries_data_basin_proc_fcst_prob_flow.date_product = '2015-02-05 12:00:00+00' AND inform_tseries_data_basin_proc_fcst_prob_flow.id_member = 1 AND temp_table_icrm_prob_flow.date_valid = inform_tseries_data_basin_proc_fcst_prob_flow.date_valid
CONTEXT:  PL/pgSQL function inform_icrm_prob_flow_query(text,integer,text,integer,integer,integer,integer,text) line 35 at EXECUTE

【问题讨论】:

  • EXECUTE query INTO curr_value;之后检查curr_value
  • curr_value 毫无疑问为空
  • 所以我们发现了您的问题。使用execute '...' into strict ...;保证查询返回数据。
  • 这似乎奏效了。谢谢!
  • @Abelisto 根据文档:If the STRICT option is given, an error is reported unless the query produces exactly one row。你解决的是一个症状,而不是一个问题。

标签: postgresql plpgsql dynamic-sql psql


【解决方案1】:

对于小题外话感到抱歉。您的代码非常不可读(并且 SQL 注入易受攻击)。您可以使用一些技术:

  1. 使用子句USING of EXECUTE 语句作为常用参数。

    DO $$
    DECLARE
      tablename text := 'mytab';
      from_date date := CURRENT_DATE;
    BEGIN
       EXECUTE 'INSERT INTO ' || quote_ident(tablename) || ' VALUES($1)'
          USING from_date;
    END
    $$;
    

    此代码将是安全的(由于使用 quote_ident 函数),速度更快(由于使用了 from_date 变量的二进制值 - 删除了多个字符串日期转换并且更具可读性(因为字符串表达式更短)。

  2. 使用函数format。构建查询字符串将更短且更易读(表别名也有帮助):

    query := format('
    SELECT value
       FROM %I _dtn
            INNER JOIN temp_table_icrm_prob_flow t ON t.date_valid = _dtn.date_valid
      WHERE _dtn.id_location = $1
        AND _dtn.date_product = $2
        AND _dtd.id_member = $3'
                     , tablename);
     EXECUTE query INTO curr_value USING location_id, product_date, count;
    

使用命名为重要的 SQL 关键字和标识符的变量是错误的想法 - 名称 countvalues 是错误的。

错误消息是干净的 - 您正在使用标识符 temp_table_icrm_prob_flow.date_valid,但查询中未提及表 temp_table_icrm_prob_flow。查询缺少 JOIN 部分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-12
    • 1970-01-01
    • 1970-01-01
    • 2011-02-10
    • 2015-02-02
    • 2015-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多