【问题标题】:How can I access a value when inserting into a table?插入表时如何访问值?
【发布时间】:2019-11-08 10:03:27
【问题描述】:

我正在尝试编写一个 java sql 查询,简化后的表将是 table(name,version),在 (name, version) 上有一个唯一约束。

我正在尝试使用条件语句在我的数据库中插入一行。意思是当存在同名的条目时,应该插入同名的行,并且版本增加1。

我尝试了以下方法:

INSERT INTO table(name,version) 
VALUES(?, CASE WHEN EXISTS(SELECT name from table where name=?)
THEN (SELECT MAX(version) FROM table WHERE name = ?) +1
ELSE 1 END)

值由用户发送。

我的问题是,如何访问值中的“名称”以便比较它们?

【问题讨论】:

    标签: java sql postgresql


    【解决方案1】:

    如果你想把它写成一个查询:

    INSERT INTO table (name, version) 
        SELECT ?, COLAESCE(MAX(t2.version) + 1, 1)
        FROM table t2
        WHERE t2.name = ?;
    

    也就是说,这很危险。两个线程可以“同时”执行此查询,并可能创建相同的版本号。您可以通过在 (name, version) 上添加唯一索引/约束来防止这种情况发生。

    使用唯一索引/约束,如果发生冲突,其中一个更新将失败。

    【讨论】:

      【解决方案2】:

      我看到至少两种方法: 1.对于每对名称和版本,您首先查询最大版本:

      SELECT MAX(VERSION) as MAX FROM <table> WHERE NAME = <name>
      

      然后你用相应的插入查询插入结果 + 1:

      INSERT INTO <table>(NAME,VERSION) VALUES (<name>,result+1)
      

      这种方法非常直接、易于阅读和实施,但是由于需要进行太多查询,因此性能并不高。

      1. 您可以通过 sql 分析和窗口函数单独使用 sql 来实现,例如:

        SELECT NAME, ROW_NUMBER() over (partition BY NAME ORDER BY NAME) as VERSION FROM&lt;table&gt;

      然后您可以使用CREATE TABLE as SELECT... 将此查询的结果保存为表 (这里的假设是第一个版本是 1,如果不是这种情况,那么可以稍微修改查询)。即使对于大型数据集,此解决方案也会非常高效。

      【讨论】:

      • 感谢您的回答,但我认为我表达的不够清楚。这 ? in values 是一个占位符(我认为这就是它在 postgres 中的名称)。因此,当用户发送示例请求时,即 name = horse, version=2, ?被 'horse' 替换,但我无法在以下 CASE 语句中访问此值。当我使用问题中的示例运行它时( where name = ?)我得到的错误是没有足够的查询参数。
      【解决方案3】:

      您应该在插入之前获取名称。在您的情况下,如果出现问题,那么您将如何知道它,以便在插入查询之前获得名称。

      不确定,但你试试这个:

      declare int version;
      if exists(SELECT name from table where name=?)
      then
      version = SELECT MAX(version) FROM table WHERE name = ?
      version += 1
      else
      version = 1
      end
      

      问候。

      【讨论】:

      • 那是非标准的 SQL。请提及适用的数据库产品。
      • 是的,@a_horse_with_no_name 你是对的,但这只是建议,所以他可能会得到一些指示。问候。
      【解决方案4】:

      这实际上是一个糟糕的计划,您可能正在更改用户指定的数据。这可能不是我们想要的,也许他们不是在尝试创建一个新版本,只是不知道想要的版本已经存在。但是,您可以创建一个函数,您的 java 调用该函数不仅插入请求的版本,如果请求的版本已经存在,则插入 max+1。此外,它返回插入的实际值。

      -- create table
      create table nv( name text
                     , version integer
                     , constraint nv_uk unique (name, version)
                     );   
      
      -- function to create version or 1+max if requested exists 
      create or replace function new_version 
                               ( name_in text
                               , version_in integer
                               )
       returns record
       language plpgsql strict
       as $$
      declare 
           violated_constraint text;
           return_name_version record;
       begin 
           insert into nv(name,version) 
                values (name_in,version_in) 
             returning (name, version) into return_name_version;
           return return_name_version;
      
      exception 
         when  unique_violation
         then  
               GET STACKED DIAGNOSTICS violated_constraint = CONSTRAINT_NAME;
               if violated_constraint like '%nv\_uk%'
               then 
                   insert into nv(name,version)
                        select name_in, 1+max(version)
                          from nv
                         where name = name_in
                         group by name_in
                     returning (name, version) into return_name_version;
                   return return_name_version;
               end if;
      end; 
      $$;     
      
      -- create some data
      insert into nv(name,version)              
         select 'n1', gn
           from generate_series( 1,3) gn ;
      
      -- test insert existing 
      select new_version('n2',1);
      select new_version('n1',1);
      
      select * 
        from nv
      order by name, version; 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多