mysql - 如何从相乘结果中选择随机唯一值

标签 mysql sql database

我有一个系统,用户可以通过不同类型的贡献获得 1 个或多个积分。它们存储在 2 个表中:

CREATE TABLE user_contribution_types (
  type_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  title VARCHAR(255) NOT NULL,
  credits DECIMAL(5,2) UNSIGNED NOT NULL,
  valid TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,

  PRIMARY KEY (type_id)
);

CREATE TABLE user_contributions (
  user_id INTEGER UNSIGNED NOT NULL,
  type_id INTEGER UNSIGNED NOT NULL,
  create_date DATETIME NOT NULL,
  valid TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,

  FOREIGN KEY (user_id)
    REFERENCES users(user_id),
  FOREIGN KEY (type_id)
    REFERENCES user_contribution_types(type_id)
);

我可以通过以下方式选择自特定日期以来获得的总积分:

SELECT SUM(credits) AS total
FROM   user_contribution_types AS a
JOIN   user_contributions AS b ON a.type_id = b.type_id
WHERE  b.create_date >= '2017-05-01 00:00:00'
       AND a.valid = TRUE
       AND b.valid = TRUE

同样,我可以包含 b.user_id 的匹配项来查找该特定用户的总积分。

我想要做的是将获得的每个积分视为赠品,并从总数中选择 3 个随机(唯一)user_id。因此,如果一名用户获得了 26 个积分,他们将有 26 次获胜机会。

如何使用 SQL 来完成此操作,还是在应用程序级别执行此操作更有意义?我更喜欢一个尽可能接近真正随机的解决方案。

最佳答案

您可以通过计算累积分布并使用rand()来选择一个用户:

SELECT uc.*
FROM (SELECT uc.user_id, (@t := @t + total) as running_total
      FROM (SELECT uc.user_id, SUM(credits) as total
            FROM user_contribution_types ct JOIN
                 user_contributions c
                 ON ct.type_id = c.type_id
            WHERE c.create_date >= '2017-05-01' AND ct.valid = TRUE AND c.valid = TRUE
            GROUP BY uc.user_id
           ) uc CROSS JOIN
           (SELECT @t := 0) params
      ORDER BY rand()
     ) uc
WHERE rand()*@t BETWEEN (running_total - total) AND running_total;

如果 rand() 恰好位于边界上,则返回两个值的可能性很小。对于您的目的来说,这不是问题;您只需添加限制 1即可。

要将其扩展到多行,您只需将 WHERE 子句修改为:

WHERE rand()*@t BETWEEN (running_total - total) AND running_total OR
      rand()*@t BETWEEN (running_total - total) AND running_total OR
      rand()*@t BETWEEN (running_total - total) AND running_total

问题是所有结果值可能都是相同的结果。

您可以随机选择三个以上的值。我倾向于选择一个更大的数字,例如 9:

WHERE 0.1*@t BETWEEN (running_total - total) AND running_total OR
      0.2*@t BETWEEN (running_total - total) AND running_total OR
      0.3*@t BETWEEN (running_total - total) AND running_total OR
      . . .
ORDER BY rand()  -- redundant, but why not?
LIMIT 3

或更简单地说:

WHERE FLOOR( 10*(running_total - total)/@t)) <> FLOOR( 10*running_total/@t)
ORDER BY rand()
LIMIT 3

这更容易,因为您可以更改 10 并沿累积分布测试任意数量的等距点。

关于mysql - 如何从相乘结果中选择随机唯一值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43867354/

相关文章:

MySQL 从 CSV 数据加载 NULL 值

mysql - 拉取组中特定列的最大值的mysql行

database - node.js sequelize 在迁移时没有主键

mysql - 如何获取每行SQL中特定值出现的次数

Mysql timediff 与 min

mysql - 优化数据库更新

SQL Server 2008无法定义小数点后4位的十进制类型?

mysql - Laravel 连接查询返回空列

mysql - 外键问题-mysql

database - 如何在 PostgreSQL 窗口分区中执行过滤查询?