postgresql postgres中的列引用'updated_at'不明确

u59ebvdq  于 2023-06-29  发布在  PostgreSQL
关注(0)|答案(2)|浏览(117)

我试着写一篇文章:

DROP FUNCTION IF EXISTS upsert_post;
CREATE OR REPLACE FUNCTION upsert_post(
    title text,
    content text,
    short_id text DEFAULT NULL,
    image text DEFAULT NULL,
    status post_status DEFAULT 'draft',
    updated_at timestamptz DEFAULT NULL,
    published_at timestamptz DEFAULT NULL,
    created_at timestamptz DEFAULT NULL
)
RETURNS SETOF posts
AS $$
DECLARE
    post_id uuid;
BEGIN
    -- Get the id based on the provided short_id
    SELECT id INTO post_id FROM posts as p 
    WHERE p.short_id = upsert_post.short_id;

    -- Upsert the post and return the changed row
    RETURN QUERY
    INSERT INTO posts as i (
        id,
        status,
        title,
        content,
        image,
        updated_at,
        published_at,
        created_at
    )
    VALUES (
        COALESCE(post_id, uuid_generate_v4()),
        upsert_post.status,
        upsert_post.title, 
        upsert_post.content,
        upsert_post.image,
        COALESCE(upsert_post.updated_at, now()),
        COALESCE(upsert_post.published_at, now()),
        COALESCE(upsert_post.created_at, now())
    )
    ON CONFLICT (id, updated_at) DO UPDATE
    SET
        title = EXCLUDED.title,
        content = EXCLUDED.content,
        image = COALESCE(EXCLUDED.image, i.image),
        updated_at = COALESCE(EXCLUDED.updated_at, i.updated_at),
        published_at = COALESCE(EXCLUDED.published_at, i.published_at),
        created_at = COALESCE(EXCLUDED.created_at, i.created_at)
    RETURNING *;
END;
$$ LANGUAGE plpgsql;

但我得到了错误:

{
  code: '42702',
  details: 'It could refer to either a PL/pgSQL variable or a table column.',
  hint: null,
  message: 'column reference "updated_at" is ambiguous'
}

我尽可能地使用化名。我怎样才能在不使用前缀i_或类似的东西重命名我的upsert_post参数的情况下使其工作?
J型

rbl8hiat

rbl8hiat1#

我需要熟练的功能,作为用户类型和功能提供和创建表也将有很大帮助
简而言之,永远不要像列名那样命名变量,这样postgres就不会有区分它们的问题了
short_id背后的想法我只能猜测。但我认为你也应该输入它,当插入一个新的行

CREATE TABLe posts (id text PRIMARY KEY,    
    status text DEFAULT 'draft',
  title text,
  content text,
    short_id text DEFAULT NULL,
    image text DEFAULT NULL,
    
    updated_at timestamptz DEFAULT NULL,
    published_at timestamptz DEFAULT NULL,
    created_at timestamptz DEFAULT NULL,
  UNIQUE (id, updated_at)
)
CREATE TABLE
CREATE OR REPLACE FUNCTION upsert_post(
    f_title text,
    f_content text,
    f_short_id text DEFAULT NULL,
    f_image text DEFAULT NULL,
    f_status text DEFAULT 'draft',
    f_updated_at timestamptz DEFAULT NULL,
    f_published_at timestamptz DEFAULT NULL,
    f_created_at timestamptz DEFAULT NULL
)
RETURNS SETOF posts
AS $$
DECLARE
    post_id uuid;
BEGIN
    -- Get the id based on the provided short_id
    SELECT id INTO post_id FROM posts as p 
    WHERE p.short_id = f_short_id;

    -- Upsert the post and return the changed row
    RETURN QUERY
    INSERT INTO posts as i (
        id,
        status,
        title,
        content,
        image,
        updated_at,
        published_at,
        created_at
    )
    VALUES (
        COALESCE(post_id, gen_random_uuid ()),
        f_status,
        f_title, 
        f_content,
        f_image,
        COALESCE(f_updated_at, now()),
        COALESCE(f_published_at, now()),
        COALESCE(f_created_at, now())
    )
    ON CONFLICT (id, updated_at) DO UPDATE
    SET
        title = EXCLUDED.title,
        content = EXCLUDED.content,
        image = COALESCE(EXCLUDED.image, i.image),
        updated_at = COALESCE(EXCLUDED.updated_at, i.updated_at),
        published_at = COALESCE(EXCLUDED.published_at, i.published_at),
        created_at = COALESCE(EXCLUDED.created_at, i.created_at)
    RETURNING *;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION
SELECT
    routine_name
FROM 
    information_schema.routines
WHERE 
    routine_type = 'FUNCTION'
AND
    routine_schema = 'public';
routine_name
upsert_post
SELECT 1
SELECT upsert_post('A'::TEXT,'b'::TEXT,'C'::TEXT,'D'::TEXT,'draft'::TEXT,NOW(),NOW(), NOW())
upsert_post
(f9bb1bd4-62f7-4380-b088-5185db0fca40,draft,A,b,,D,"2023-06-25 18:50:17.61884+01","2023-06-25 18:50:17.61884+01","2023-06-25 18:50:17.61884+01")
SELECT 1
SELECT * FROM posts

| 地位|标题|内容|短ID|影像|更新_at|发表于|创建于| created_at |
| - -----|- -----|- -----|- -----|- -----|- -----|- -----|- -----| ------------ |
| 吃水|一个|B|联系我们|D级|2023-06-25 18:50:17.61884+01| 2023-06-25 18:50:17.61884+01| 2023-06-25 18:50:17.61884+01| 2023-06-25 18:50:17.61884+01 |

SELECT 1

fiddle

2izufjch

2izufjch2#

这里的问题是输入参数与ON CONFLICT参数冲突。
我很惊讶人们是如何毫无理由地冷漠地否决你的帖子的。我终于找到了三个真实的的答案。

1. #variable_conflict

Postgres有三个变量可用于此用例:

#variable_conflict error
#variable_conflict use_variable
#variable_conflict use_column

但我得到了它的工作感谢上面提供的变量替换文档:

RETURNS SETOF posts
AS $$
#variable_conflict use_column
DECLARE
    post_id uuid;
BEGIN

2.论约束冲突

我上面代码的问题是ON CONFLICT不允许使用别名。如果你知道一个方法,请让我知道。否则,我只是简单地使用了复合约束的名称ON CONSTRAINT

ON CONFLICT ON CONSTRAINT posts_pkey DO UPDATE

编辑:也可以在schema中任意命名约束:

CREATE TABLE posts (
  ...
  CONSTRAINT id_updated_at_pk PRIMARY KEY (id, updated_at)
  ...
);

3.创建子函数

基本上,你可以创建一个函数_upsert_post(),它接受前缀参数。然后将你的非前缀参数传入这个函数:

upsert_post

RETURN QUERY SELECT * FROM _upsert_post(id, status, title...);

说你必须使用prefixes是不正确的。在我的例子中,用户必须更改前端代码,以使用与后端代码不同的参数。这对团队来说可能很重要。
或者,您可以限定不明确的引用以使其清晰。
这是我写这篇文章的目的,我觉得很清楚。你不需要我的完整模式来理解这个问题。一旦有人否决了一个帖子,其他人就会认为这不是一个有效的问题。请多加留意。
谢谢
J型

相关问题