Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/59.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 如何从相乘结果中选择随机唯一值_Mysql_Sql_Database - Fatal编程技术网

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

Mysql 如何从相乘结果中选择随机唯一值,mysql,sql,database,Mysql,Sql,Database,我有一个系统,用户可以为不同类型的贡献赚取1个或多个学分。它们存储在两个表中: 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

我有一个系统,用户可以为不同类型的贡献赚取1个或多个学分。它们存储在两个表中:

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\u id
来查找该特定用户的总积分

我想做的是将获得的每个积分作为赠品的一个条目,从总数中随机选择3个(唯一的)
user\u id
s。因此,如果一个用户获得了26个积分,他们将有26次获胜的机会


如何使用SQL实现这一点,或者在应用程序级别实现这一点更有意义吗?我更喜欢一个尽可能接近真正随机的解决方案。

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

如果
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*(总运行量-总运行量)/@t))楼层(10*总运行量/@t)
兰德订单()
限制3

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

好吧,我无法让Gordon的代码无误运行,因此我最终返回到应用程序逻辑并遵循解决方案。例如:

// pick a random winner since a given date
// optionally exclude certain users
public function getWinner($date, array $exclude = []) {
    if (!empty($exclude)) {
        $in = implode(',', array_fill(0, count($exclude), '?'));
        array_unshift($exclude, $date);

        $sql = "SELECT   b.user_id, 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 >= ?
                         AND b.user_id NOT IN ($in)
                         AND a.valid = TRUE
                         AND b.valid = TRUE
                GROUP BY b.user_id";
        $sth = $this->db->prepare($sql);
        $sth->execute($exclude);
    } else {
        $sql = "SELECT   b.user_id, 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 >= :date
                         AND a.valid = TRUE
                         AND b.valid = TRUE
                GROUP BY b.user_id";
        $sth = $this->db->prepare($sql);
        $sth->execute([':date' => $date]);
    }

    $result = [];
    while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
        $result[$row['user_id']] = floor($row['total']);
    }

    // cryptographically secure pseudo-random integer, otherwise fallback
    $total = array_sum($result);
    if (function_exists('random_int')) {
        $rand = $total > 0 ? random_int(0, $total - 1) : 0;
    } else {
        // fallback, NOT cryptographically secure
        $rand = $total > 0 ? mt_rand(0, $total - 1) : 0;
    }

    $running_total = 0;
    foreach ($result as $user_id => $credits) {
        $running_total += $credits;
        if ($running_total > $rand) {
            // we have a winner
            return $user_id;
        }
    }

    return false;
}
因此,只要我想选择多个赢家,我基本上可以多次执行此代码:

$ts = '2017-01-01 00:00:00';
$first_place = getWinner($ts);
$second_place = getWinner($ts, [$first_place]);
$third_place = getWinner($ts, [$first_place, $second_place]);

除非发布替代解决方案,否则我将接受此答案。

如果我理解正确,您的第一个示例可以与应用程序级逻辑结合使用?所以我可以把它包装成一个函数,然后把结果传给它自己?例如,
用户id不在($winners)
。这将完全排除重复的结果…我也得到了这个错误:“字段列表”@mistermartin中的未知列“uc.user\u id”。你的问题中指定了该列。您可能丢失了
u.user\u id
,但我修复了别名。是的,我正在更改您的示例
userId
userId
,以匹配我的示例,
user\u id
。它仍在产生相同的错误。我很愿意接受这个答案,但我无法克服提到的错误。我在phpmyadmin(4.0.10deb1)中执行此操作:“字段列表”中的未知列“uc.user\u id”,即使该列实际上存在。
$ts = '2017-01-01 00:00:00';
$first_place = getWinner($ts);
$second_place = getWinner($ts, [$first_place]);
$third_place = getWinner($ts, [$first_place, $second_place]);