postgresql - PL/pgSQL函数随机选择一个id

标签 postgresql plpgsql

目标:

  1. 使用顺序 ID 列表预填充表,例如1 到 1,000,000。该表还有一个可以为空的附加列。 NULL 值标记为未分配,非 NULL 值标记为已分配
  2. 我可以调用该函数,该函数要求从表中随机选择 x 个尚未分配的 ID。

这是针对一些非常具体的事情,虽然我知道有不同的方法可以做到这一点,但我想知道是否有针对此特定实现中的缺陷的解决方案。

我有一些部分有效的东西,但想知道该功能的缺陷在哪里。

这是表格:

CREATE SEQUENCE accounts_seq MINVALUE 700000000001 NO MAXVALUE;

CREATE TABLE accounts (
  id BIGINT PRIMARY KEY default nextval('accounts_seq'), 
  client VARCHAR(25), UNIQUE(id, client)
);

此函数gen_account_ids只是一次性设置,用于用固定数量的行预先填充表,所有行都标记为未分配

/*
  This function will insert new rows into the accounts table with ids being
  generated by a sequence, and client being NULL.  A NULL client indicates
  the account has not yet been assigned.
*/
CREATE OR REPLACE FUNCTION gen_account_ids(bigint)
  RETURNS INT AS $gen_account_ids$
DECLARE
  -- count is the number of new accounts you want generated
  count alias for $1;
  -- rowcount is returned as the number of rows inserted
  rowcount int;
BEGIN
  INSERT INTO accounts(client) SELECT NULL FROM generate_series(1, count);
  GET DIAGNOSTICS rowcount = ROW_COUNT;
  RETURN rowcount;
END;
$gen_account_ids$ LANGUAGE plpgsql;

因此,我使用它来预先填充表,例如 1000 条记录:

SELECT gen_account_ids(1000);

下一个函数assign旨在随机选择一个未分配 id(未分配意味着client列为空),并用客户值(value)因此被分配。它返回受影响的行数。

有时它会起作用,但我确实相信会发生冲突 - 这就是为什么我尝试使用DISTINCT,但它返回的行数通常少于所需的行数。例如,如果我 select allocate(100, 'foo'); 它可能会返回 95 行,而不是所需的 100 行。

如何修改它以使其始终返回所需的确切行?

   /*
     This will assign ids to a client randomly
     @param int is the number of account numbers to generate
     @param varchar(10) is a string descriptor for the client
     @returns the number of rows affected -- should be the same as the input int

     Call it like this: `SELECT * FROM assign(100, 'FOO')`
   */
   CREATE OR REPLACE FUNCTION assign(INT, VARCHAR(10))
     RETURNS INT AS $$
   DECLARE
     total ALIAS FOR $1;
     clientname ALIAS FOR $2;
     rowcount int;
   BEGIN
     UPDATE accounts SET client = clientname WHERE id IN (
       SELECT DISTINCT trunc(random() * (
         (SELECT max(id) FROM accounts WHERE client IS NULL) - 
         (SELECT min(id) FROM accounts WHERE client IS NULL)) + 
         (SELECT min(id) FROM accounts WHERE client IS NULL)) FROM generate_series(1, total));
     GET DIAGNOSTICS rowcount = ROW_COUNT;
     RETURN rowcount;
   END;
   $$ LANGUAGE plpgsql;

这大致基于 this您可以在其中执行类似 SELECT trunc(random() * (100 - 1) + 1) FROMgenerate_series(1,5); 的操作,它将选择 1 到 100 之间的 5 个随机数。

我的目标是做类似的事情,在最小和最大未分配行之间选择一个随机 ID,并将其标记为更新。

最佳答案

这不是最好的答案,因为它确实涉及全表扫描,但在我的情况下,我不关心性能,而且它有效。这是基于 @CraigRinger 对博客文章 getting random tuples 的引用。

我通常有兴趣了解其他(也许更好)的解决方案 - 并且特别好奇为什么原始解决方案不足,以及 @klin 还设计了什么。

所以,这是我的强力随机顺序解决方案:

-- generate a million unassigned rows with null client column
insert into accounts(client) select null from generate_series(1, 1000000);

-- assign 1000 random rows to client 'foo'
update accounts set client = 'foo' where id in 
  (select id from accounts where client is null order by random() limit 1000);

关于postgresql - PL/pgSQL函数随机选择一个id,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32727834/

相关文章:

database - 在 plpgsql 触发器函数中访问 OLD.myLoopVariable/NEW.myLoopVariable?

javascript - 如何从 Array.map() 函数正确返回异步数据

json - row_to_json 将 "NULL"导出为 "None"

sql - 在 INSERT 语句的 RETURNING 中包含表名

java - org.postgresql.util.PSQLException : ERROR: column "geo_detail" is of type point but expression is of type bytea any solution please?

postgresql - 无法执行 plpgsql/postgres 中的函数

sql - 从查询结果表中读取列数及其类型(C语言)

java - Postgres错误: a column definition list is only allowed for functions returning "record"

postgresql - 在事务 : truncate vs delete or upsert/merge 中包装 postgresql 命令

c# - PostgreSQL 错误 : query string argument of EXECUTE is null