【问题标题】:Adding to integer value with update or insert statement使用更新或插入语句添加到整数值
【发布时间】:2021-03-23 16:58:08
【问题描述】:

我正在尝试在 Firebird 2.5 中创建一个 SQL 语句,以便通过在记录存在时添加到整数来更新整数数量,或者只是插入一个新行。我似乎无法找到关于添加某个数量(如果已经存在)的具体问题的真正答案。

我能想到的SQL如下:

UPDATE OR INSERT INTO Pencils(Number, Location, Quantity)
VALUES('12345678/90', 'TOP', 20)
MATCHING(Number, Location);

所以如果有匹配的记录,我想在实际 20 数量的基础上增加一定数量。

就像我对UPDATE 声明所做的那样:

UPDATE Pencils SET Quantity = Quantity + 20 WHERE Number = '12345678/90' 

是否有可能使用UPDATE OR INSERT 语句实现这样的结果,还是有其他方法? . .

编辑: 使用 IBExpert / Delphi 使用参数查询的解决方案

MERGE INTO Pencils AS Dest
USING (SELECT CAST(:myNumb as varchar(30))  AS Number,
              CAST(:myLoca as varchar(10))  AS Location,
              CAST(:myQuan as integer)  AS Quantity
        FROM RDB$DATABASE) Src
    ON (Dest.Number = Src.Number AND Dest.Location = Src.Location)
WHEN MATCHED THEN
   UPDATE SET Dest.Quantity = Dest.Quantity + Src.Quantity
WHEN NOT MATCHED THEN
   INSERT (Number, Location, Quantity) VALUES (Src.Number, Src.Location, Src.Quantity);

【问题讨论】:

  • 对于完全怪胎来说,还有另一种古老的方法可以做到这一点,它早于 MERGE、U-O-I 甚至 Firebird 本身。它需要创建可更新视图,其中U/I/D 操作由triggers 中的PSQL 代码实现,然后Before Insert 触发器将检查基础表中是否已经存在行。但这将是这样一台 Rube Goldberg 机器......像 Delphi XE2 中的 NormalizeRect 一样可爱迷人:-D

标签: sql firebird firebird2.5


【解决方案1】:

使用 Firebird 的 MERGE 处理复杂的 UPSERT:

MERGE INTO Pencils AS Dest
USING (SELECT '12345678/90' AS Number,
              'TOP'         AS Location,
              20            As Quantity
         FROM RDB$DATABASE) Src
   ON (Dest.Number = Src.Number AND Dest.Location = Src.Location)
WHEN MATCHED THEN
   UPDATE SET Dest.Quantity = Dest.Quantity + 10
WHEN NOT MATCHED THEN
   INSERT (Number, Location, Quantity) VALUES (Src.Number, Src.Location, Src.Quantity);

这里唯一的复杂性是您在更新时动态计算的Quantity。如果您只是用静态值覆盖现有记录,则可以更简洁地使用 UPDATE OR INSERT。

示例:

SQL> SELECT * FROM Pencils;

NUMBER               QUANTITY LOCATION         
================ ============ ================ 
123                         5 TOP              
123                        10 MID              

SQL> MERGE INTO Pencils AS Dest
CON> USING (SELECT '123' AS Number, 'TOP' AS Location, 20 As Quantity
CON> FROM RDB$DATABASE) Src ON (Dest.Number = Src.Number AND Dest.Location = Src.Location)
CON> WHEN MATCHED THEN
CON>    UPDATE SET Dest.Quantity = Dest.Quantity + 10
CON> WHEN NOT MATCHED THEN
CON>    INSERT (Number, Location, Quantity) VALUES (Src.Number, Src.Location, Src.Quantity);

SQL> SELECT * FROM Pencils;

NUMBER               QUANTITY LOCATION         
================ ============ ================ 
123                        15 TOP              
123                        10 MID              

SQL> SELECT * FROM Pencils;

NUMBER               QUANTITY LOCATION         
================ ============ ================ 
123                         5 TOP              
123                        10 MID              

SQL> MERGE INTO Pencils AS Dest
CON> USING (SELECT '123' AS Number, 'TOP' AS Location, 20 As Quantity
CON> FROM RDB$DATABASE) Src ON (Dest.Number = Src.Number AND Dest.Location = Src.Location)
CON> WHEN MATCHED THEN
CON>    UPDATE SET Dest.Quantity = Dest.Quantity + 10
CON> WHEN NOT MATCHED THEN
CON>    INSERT (Number, Location, Quantity) VALUES (Src.Number, Src.Location, Src.Quantity);

SQL> SELECT * FROM Pencils;

NUMBER               QUANTITY LOCATION         
================ ============ ================ 
123                        15 TOP              
123                        10 MID              
123                        20 BOT       

【讨论】:

  • 如果我理解你的说法正确,如果记录不存在,它将插入 20 作为数量,但它应该插入 10(10 是我将从 Delphi 程序获得的动态值,这将是数量插入)
  • @VirussInside 然后将语句更改为使用 10 而不是 20。
  • 但我认为 Firebird 不允许在 USING 语句中没有 FROM 的 SELECT
  • @virussinside 坦率地说,这是一个不同的问题,可能有人回答了数百万次。这是准备查询时的先有鸡还是先有蛋的问题,FB 在知道数据类型和最终确定输出行集的二进制缓冲区布局之前无法开始准备,并且在开始准备之前无法推断未知数据类型。所以你应该在之前硬编码参数数据类型。 Select ? From RDB$DATABASE 通常会失败,但 Select cast( ? as integer) From RDB$DATABASE 会起作用。击中它的荣誉。我在埋伏中等待,想知道你是否在没有参数的情况下偷懒。
  • 另外,Firebird中没有varchar这样的数据类型!您必须指定长度
【解决方案2】:

当然可以使用更新或插入。 但是您必须重复您的键值,这会使您的查询变得脆弱(仅更改两个值之一 - 并且您的查询行为不端)

UPDATE OR INSERT 
   INTO Pencils(Number, Location, Quantity)
VALUES( '12345678/90', 'TOP', 
  20 + coalesce(
        ( Select Quantity 
             From Pencils 
          where Number = '12345678/90' 
            and Location = 'TOP' )
       ,0 )
       )
MATCHING(Number, Location);

在 Delphi 中,大多数库不支持具有相同名称的多个 SQL 参数,因此您可能必须实际创建两个参数,名称相似但名称清晰,才能使这种方法起作用。

所以,总而言之,MERGE 会是更好的方法,更安全。但为了完整起见,这也应该有效。

select rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version
     , rdb$character_set_name
from rdb$database;
版本 | RDB$CHARACTER_SET_NAME :-------- | :------------------------------------------------ -------------------------------------------------- ---------------------- 3.0.5 | UTF8
create table Pencils( Number varchar(20), Location varchar(10), Quantity int,
   constraint pk_pens primary key (number, location) )
✓
INSERT INTO Pencils(Number, Location, Quantity)
VALUES('12345678/90', 'TOP', 13)

1 行受影响

INSERT INTO Pencils(Number, Location, Quantity)
VALUES('12345678/90', 'MID', 7)

1 行受影响

select * from pencils
号码 |位置 |数量 :------------ | :------- | --------: 12345678/90 |顶部 | 13 12345678/90 |中 | 7
UPDATE OR INSERT 
   INTO Pencils(Number, Location, Quantity)
VALUES( '12345678/90', 'TOP', 
  20 + coalesce(
          (Select Quantity 
             From Pencils 
          where Number = '12345678/90' 
            and Location = 'TOP')
       ,0 )
       )
MATCHING(Number, Location);

1 行受影响

UPDATE OR INSERT 
   INTO Pencils(Number, Location, Quantity)
VALUES( '12345678/90', 'BOTTOM', 
  20 + coalesce(
          (Select Quantity 
             From Pencils 
          where Number = '12345678/90' 
            and Location = 'BOTTOM')
       ,0 )
       )
MATCHING(Number, Location);

1 行受影响

select * from pencils
号码 |位置 |数量 :------------ | :------- | --------: 12345678/90 |顶部 | 33 12345678/90 |中 | 7 12345678/90 |底部 | 20

db小提琴here


@pilcrow 提问:为了完整起见,您能否显示 UPDATE OR INSERT,其中新插入的数量不是简单地“零 + 增量”?

基于表达式中的NULL 语义,只需像这样修改查询的中间部分:

.........
VALUES( '12345678/90', 'TOP', 
  coalesce( 20 + -- increment when found
        ( Select Quantity 
             From Pencils 
          where Number = '12345678/90' 
            and Location = 'TOP' )
       ,  -15 /* new value when not found */ )
       )
.......

【讨论】:

  • 为了完整起见,您能否显示更新或插入,其中新插入的数量不是只是“零 + 增量”?基于澄清 cmets,我相信 OP 确实希望新记录默认为“零 + 增量”,但所写的问题是“20 表示新记录”和“数量 += 10 表示更新”。那将是一个不同的 UP OR IN,也可能对某人有用。
  • @pilcrow 确切地说,我只有一个数量,例如 20,如果找不到数量和位置,我想要一个具有此数量的新记录,或者只是将这些相同的 20 添加到现有数量。所以它是“零 + newQuantity”或“oldQuantity += newQuantity”
  • 哦,我明白了,我没有想到 COALESCE,但你是对的,对于相同的值有不同的参数会非常脆弱。感谢您的帮助。
  • @pilcrow 完成了,但我不在 PC ATM,所以实际上并没有尝试。可能有轻微的语法错误
  • @Arioch' 与我对答案的最后评论相同的问题,是否可以通过将它们替换为 :myNumber、:myLocation 等参数来替换我们放入 SELECT 中的静态值
猜你喜欢
  • 2022-12-03
  • 2010-09-24
  • 1970-01-01
  • 2022-12-21
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多