如何插入行并附加在行号上以强制唯一性?

时间:2018-08-31 10:40:33

标签: sql postgresql window-functions

我有一个users表,其中包含电子邮件。

users同时具有user_idemail

我还有一个user_referral_codes的列表。

user_referral_codes有一个user_id,它是用户user_id上的外键。它还有一个referral_code列。

referral_code将成为用户email的第一部分。

如果电子邮件为johnsmith99@gmail.com,则referral_code将设置为johnsmith

这很好用:

`INSERT INTO flock_auth.user_referral_codes (user_id, referral_code)
VALUES($1, LEFT(LEFT($2, strpos($2, '@') - 1), 12));`

但是,可能有两封电子邮件相继添加,例如:

johnsmith@gmail.comjohnsmith@aol.com

在那种情况下,我希望第二次插入成为:

johnsmith1

对于现有用户和推荐代码的初始UPDATE,这很好:

UPDATE auth.user_referral_codes urc
    SET referral_code = (
      LEFT(LEFT(email, strpos(email, '@') - 1), 12)
      || (CASE WHEN seqnum > 1 THEN (seqnum-1)::TEXT ELSE '' END)
    )
    FROM (
      SELECT
        auth.users.*,
        row_number() OVER (
          PARTITION BY LEFT(LEFT(email, strpos(email, '@') - 1), 12)
          ORDER BY id
        ) AS seqnum
      FROM auth.users
    ) AS u
  WHERE urc.user_id = u.id;

我试图修改此代码以进行插入。

INSERT INTO auth.user_referral_codes (user_id, referral_code)
  SELECT (
    $1,
    LEFT(LEFT(email, strpos(email, '@') - 1), 12)
    || (CASE WHEN seqnum > 1 THEN (seqnum-1)::TEXT ELSE '' END)
    )
    FROM (
      SELECT
        auth.users.*,
        row_number() OVER (
          PARTITION BY LEFT(LEFT(email, strpos(email, '@') - 1), 12)
          ORDER BY id
        ) AS seqnum
      FROM auth.users
    ) AS u
  WHERE $1 = u.id;

错误返回为:INSERT has more target columns than expressions

我该如何做?

2 个答案:

答案 0 :(得分:1)

不用输入您的代码,理论上我会做这样的事情:

with parsed as (
  select
    email,
    left (split_part (email, '@', 1), 12) as user_id,
    row_number() over
        (partition by left (split_part (email, '@', 1), 12) order by id) - 1 as rn
  from auth.users
)
select
  email,
  case
    when rn > 0 then user_id || rn
    else user_id
  end as user_id
from parsed

如果没有CTE,您可以脱身,但这确实使事情变得更加整洁。

答案 1 :(得分:0)

现在有效:

INSERT INTO auth.user_referral_codes (user_id, referral_code)
  SELECT
    $1,
    LEFT(LEFT(email, strpos(email, '@') - 1), 12)
    || (CASE WHEN seqnum > 1 THEN (seqnum-1)::TEXT ELSE '' END)
    FROM (
      SELECT
        auth.users.*,
        row_number() OVER (
          PARTITION BY LEFT(LEFT(email, strpos(email, '@') - 1), 12)
          ORDER BY id
        ) AS seqnum
      FROM auth.users
    ) AS u
  WHERE $1 = u.id;

问题是多余的括号和select语句的嵌套。