【问题标题】:INSERT if not exists, else return id如果不存在则插入,否则返回 id
【发布时间】:2015-05-15 07:57:26
【问题描述】:

有一些限制:

  1. 无法修改数据库
  2. 列不是唯一的
  3. 需要返回最后一个插入 id (RETURNING id)
  4. 如果存在,返回现有id
  5. 它将通过我们的自定义 db 库调用(select 中的值将作为 PHP 的参数 (?, ?, ?, !))

INSERT INTO tags (name, color, groupid)
SELECT  'test', '000000', 56
WHERE NOT EXISTS (
  SELECT text FROM tags WHERE name = 'test' AND groupid = 56
)
RETURNING id

这有效 - 直到我还需要获取现有 ID。有了这个,我只能得到插入的 id。 SELECT语句不插入是否可以返回值?

更新:

DO $$
BEGIN
    IF NOT EXISTS (
      SELECT text FROM tags WHERE name = 'test' AND groupid = 56
    )
    THEN
      INSERT INTO tags (name, color, groupid)
          VALUES  ('test', '000000', 56) RETURNING id;
    ELSE
      RETURN SELECT text FROM tags WHERE name = 'test' AND groupid = 56;
    END IF;
END
$$

正在使用这种格式进行测试 - 但是以一些错误结束:

RETURN cannot have a parameter in function returning void

【问题讨论】:

  • 您可以通过创建一个命名函数而不是 DO 语句来避免最后一个错误,该语句具有完全相同的主体,例如CREATE FUNCTION foo(text, text, int) RETURNS int LANGUAGE plpgsql AS $$ ....;然后你会以SELECT foo(?, ?, ?) AS id 的形式执行它
  • 为了测试我将它创建为一个函数,结果有点不同:'错误:查询没有结果数据的目的地'
  • 那是因为RETURNING id 就像SELECT,你需要告诉函数RETURN 整个INSERT 语句的结果,我想。
  • 嗨,我对 PostgreSql 不是很熟悉,但我有一些空闲时间想出了这个sqlfiddle.com/#!15/9f96a,所以你可以试试它是否适合你,这将插入新值和返回值 - 1 如果表中已经不存在值并且如果有值它将返回值 id

标签: php sql postgresql


【解决方案1】:

您可以使用 CTE 来做到这一点。

信息 cte 包含源数据,因此请用 PHP 的占位符替换其中的值。

如果存在现有记录,CTE 将返回联合前半部分的结果(因此将显示旧 id),如果执行了插入,则返回后半部分(因此将显示新 id)。

WITH info (name, color, groupid) AS
  (values('test','000000',56)),
  trial AS (
    INSERT INTO tags(name, color, groupid)
      SELECT info.name, info.color, info.groupid
      FROM info
      WHERE NOT EXISTS (
        SELECT * FROM tags t2
        WHERE t2.name = info.name AND t2.groupid= info.groupid)
    RETURNING id)
SELECT tags.id
FROM tags
INNER JOIN info ON tags.name = info.name AND tags.groupid= info.groupid
UNION ALL
SELECT trial.id
FROM trial;

SQL 小提琴示例:http://sqlfiddle.com/#!15/a7b0f/2

使用 CTE 的 Postgres 手册页

http://www.postgresql.org/docs/9.1/static/queries-with.html

【讨论】:

  • 几乎 - 它在 pgadmin 中工作正常,但是通过 php 调用它:错误:列“groupid”是整数类型,但表达式是文本类型 第 6 行:选择 info.name、info.color、info.groupid ^ 提示:您将需要重写或强制转换表达式。我不明白为什么。
  • 听起来您的 PHP 代码正在向组 id 列发送一个文本字符串。如果您确定它是一个 int 并且无法更改 PHP 以发送一个 int,则可以通过更改值行来修复它: (values( :name,:color,cast(:groupid as int))
  • 确实它设法将其作为字符串发送。为了确保我在其中的每一个上添加了 ::integer 转换。
【解决方案2】:

您可以使用 IF 语句来执行此操作。

IF NOT EXISTS(SELECT text FROM tags WHERE name = 'test')
BEGIN
  INSERT INTO tags (name, color, groupid)
  VALUES ('test', '000000', 56)
  SET @textId = SCOPE_IDENTITY()
END
ELSE
BEGIN
  SELECT @textId = ID FROM tags WHERE name = 'test'
END

您必须根据您的要求对上述查询进行必要的更改。

【讨论】:

  • 错误:“IF”处或附近的语法错误这甚至看起来都不是有效的语法。不应该是 IF THAN ELSE 吗?
  • 这看起来像 Microsoft SQLServer 语法
【解决方案3】:

如果name 是唯一的:

INSERT INTO `tags` (name, color, groupid) VALUES ('test', '000000', 56)
  ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), `name`='test';
SELECT LAST_INSERT_ID();

【讨论】:

  • 名称不是唯一的。可以有多个相同的名称,但有不同的 groupid。所以整个插入/选择检查都在该组的“范围”内。
  • ON DUPLICATE KEY 在 PostgreSQL 中不存在,您的查询将失败。它也会在您的特定 MySQL 查询的其他问题上失败。
猜你喜欢
  • 1970-01-01
  • 2013-08-14
  • 1970-01-01
  • 2014-04-28
  • 2013-06-18
  • 2013-12-27
  • 1970-01-01
  • 1970-01-01
  • 2017-02-10
相关资源
最近更新 更多