【问题标题】:PostgreSQL - Function with local variables - Column reference is ambiguousPostgreSQL - 具有局部变量的函数 - 列引用不明确
【发布时间】:2018-05-30 21:03:32
【问题描述】:

我查看了与我的主题相匹配的其他问题,但在我的情况下,我相信歧义来自与列同名的变量。

这是我正在尝试创建的函数的简化版本:

CREATE OR REPLACE FUNCTION get_user_id(username TEXT) 
RETURNS INTEGER AS $$
  DECLARE
    user_id BIGINT;
    other_param TEXT;
  BEGIN
    SELECT INTO user_id user_id FROM users WHERE users.username = get_user_id.username;

    SELECT INTO other_param users.value FROM users WHERE users.user_id = user_id;

    RETURN user_id;
  END
$$ LANGUAGE PLPGSQL 

问题在于WHERE 右侧的user_id 被视为对列的引用。

我肯定需要在局部变量中使用user_id,因为它将在函数中进一步的UPDATEDELETE 操作中使用,但我不会将它作为参数传递,只有用户名。

经过一些阅读并且以前在参数方面也遇到了一些问题,我意识到我可以使用get_user_id.username,但它仅适用于参数,而不适用于局部变量(如果我将它与变量一起使用,则查询失败,因为没有 FROM- get_user_id 的子句)。

因此,关于在函数内部的查询中使用变量,我是否遗漏了一些(不那么)明显的东西?

--- 编辑

抱歉,我过于简化了函数,当然有一个获取 user_id 的第一个查询,并且该部分工作正常。

【问题讨论】:

  • 你不是说WHERE users.username = username
  • 抱歉,我过度简化了我的功能。我相信现在更有意义
  • 我认为您可以通过为变量选择不同的名称来回避这个问题,即不是user_id。此外,如果 user_id 在功能上依赖于用户名,则您真的不需要存储 user_id,无论在何处使用 user_id,都可以将其替换为用户名(如果需要,通过连接用户表)
  • 是的,最终我可能不得不这样做,但我也想知道这是否是设计使然,或者我缺少一些关于使用变量的知识

标签: postgresql plpgsql postgresql-9.5


【解决方案1】:

任何局部变量都可以用块标签来限定

create table foo(a integer);
insert into foo values(10);

do $$
<<mylabel>>
declare
  a int default 5;
  r record;
begin
  select foo.a into r
    from foo
    where foo.a = mylabel.a + 5;
  raise notice '%', r.a;
end;
$$;
NOTICE:  10
DO

通常所有可能发生冲突的局部变量都有前缀——公共前缀是_

【讨论】:

    【解决方案2】:

    我给参数和变量加上前缀,这样它们就不太可能与列名冲突:

    CREATE OR REPLACE FUNCTION get_user_id (in_username TEXT) 
    RETURNS INTEGER AS $$
      DECLARE
        v_user_id BIGINT;
      BEGIN
        SELECT u.user_id  INTO v_user_id FROM users u WHERE u.user_id = in_user_id;
    
        RETURN v_user_id;
      END
    $$ LANGUAGE PLPGSQL 
    

    不过,您大概想比较用户名:

        SELECT u.user_id  INTO v_user_id FROM users u WHERE u.username = in_username;
    

    【讨论】:

      【解决方案3】:

      https://www.postgresql.org/docs/current/static/plpgsql-implementation.html

      有时修复所有不明确的引用是不切实际的 大量的 PL/pgSQL 代码。在这种情况下,您可以指定 PL/pgSQL 应该将不明确的引用解析为变量

      等等......因此:

      t=# CREATE OR REPLACE FUNCTION get_user_id(username TEXT)
      RETURNS INTEGER AS $$
      #variable_conflict use_variable
        DECLARE
          user_id BIGINT;
          other_param TEXT;
        BEGIN
          SELECT INTO user_id users.user_id FROM users WHERE users.username = username;
          RETURN user_id;
        END
      $$ LANGUAGE PLPGSQL
      ;
      CREATE FUNCTION
      

      让我们检查一下:

      t=# create table users (user_id int, username text);
      CREATE TABLE
      t=# insert into users values (1,'a');
      INSERT 0 1
      t=# select get_user_id('a');
       get_user_id
      -------------
                 1
      (1 row)
      

      注意——这个检查有很大的意义,如果你禁用它,你会得到一些非常难看和非常不可见的错误。这是相当危险的。请不要这样做。

      不要使用它,除非你看到不可见的错误或者你控制了每一行

      【讨论】:

      • 注意——这个检查有很大的意义,如果你禁用它,你会得到一些非常丑陋和非常不可见的错误。这是相当危险的。请不要这样做。
      • @PavelStehule 您认为我应该将答案删除为潜在危险?
      • 仅添加警告 - 比盲目了解问题更好。
      【解决方案4】:

      是不是缺少用户名过滤?我想你想要这样的东西:

      SELECT users.user_id INTO user_id FROM users WHERE users.username = username;
      

      这样,users.user_id 的值会在用户名过滤后设置在user_id 变量中。

      【讨论】:

        猜你喜欢
        • 2021-12-14
        • 2023-01-18
        • 2012-04-06
        • 1970-01-01
        • 1970-01-01
        • 2023-03-24
        • 1970-01-01
        • 2011-03-10
        相关资源
        最近更新 更多