0

I am attempting to create referral code infrastructure. I would like to use upto the first 12 characters of a user's email, deduped with an integer:

So given three users with the emails bob@gmail.com, bob@hotmail.com, and bob@yahoo.com, I would like to generate codes bob, bob1, and bob2.

I've done this with the following sql:

UPDATE 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
       users.*,
        row_number() OVER (
          PARTITION BY LEFT(LEFT(email, strpos(email, '@') - 1), 12)
          ORDER BY id
        ) AS seqnum
      FROM users
    ) AS u
  WHERE urc.user_id = u.id;

This works fine, however there are two edgecases:

1) The referral codes must be globally unique, so adding the users bob1@gmail.com and bob2@gmail.com into the mix should result in the following pairings:

bob@gmail.com | bob
bob@hotmail.com | bob3
bob@yahoo.com | bob4
bob1@gmail.com | bob1
bob2@gmail.com | bob2

How can I account for this?

2) There is a second code, coupon_codes. How do I also ensure no duplication with this table?

e.g. if a coupon_code bob2 were to exist, then bob2@gmail.com should also get referral code bob21?

Sample schema:

    CREATE TABLE users(
  id                    bigint                      NOT NULL PRIMARY KEY,
  email                 TEXT                        NOT NULL UNIQUE
 );

CREATE TABLE user_referral_codes(
  user_id                 bigint          NOT NULL REFERENCES users (id),
  referral_code           TEXT
);

CREATE TABLE coupon_codes(
  code_id                 TEXT
);

INSERT INTO users(id, email) VALUES(1, 'bob@flockcover.com');
INSERT INTO users(id, email) VALUES(2, 'bob@google.com');
INSERT INTO users(id, email) VALUES(3, 'bob@gmail.com');
INSERT INTO users(id, email) VALUES(4, 'bob1@gmail.com');

INSERT INTO user_referral_codes(user_id) VALUES(1);
INSERT INTO user_referral_codes(user_id) VALUES(2);
INSERT INTO user_referral_codes(user_id) VALUES(3);
INSERT INTO user_referral_codes(user_id) VALUES(4);

INSERT INTO coupon_codes(code_id) VALUES('bob2');

Sample select query demonstrating current (incomplete) behavior:

SELECT LEFT(LEFT(email, strpos(email, '@') - 1), 12)
  || (CASE WHEN seqnum > 1 THEN (seqnum-1)::TEXT ELSE '' END)
  FROM (
    SELECT
      users.*,
      row_number() OVER (
        PARTITION BY LEFT(LEFT(email, strpos(email, '@') - 1), 12)
        ORDER BY id
      ) AS seqnum
    FROM users
  ) as u;

sqlfiddle link demonstrating problem: http://sqlfiddle.com/#!17/0a6ea

Abraham P
  • 15,029
  • 13
  • 58
  • 126
  • So if first three "bob"s are created you want "bob", "bob1" and "bob2". Then, if after a while a "bob1" comes along, you want to rename the previous "bob1" to "bob3" and keep the new "bob1" as it is? – sticky bit Sep 03 '18 at 21:50
  • No so this is logic for initial population of data, and will go into a migration. If a bob1 comes along after a while, his code should be bob11 (followed by bob12 for the next one, etc) Neither a users referral code nor coupon codes should ever be modified once written – Abraham P Sep 03 '18 at 21:55

0 Answers0