2010-01-15 42 views
12

我想建议登录的用户,如果他们的第一选择已被接受。假设用户想要注册为“超人”。现场已经有一些超人。以“Superman01”,“Superman02”等形式建议登录。因此,脚本必须:在DB登录建议查询

  • 支票“超人”登陆
  • 如果已被使用,追加“01”登录并在DB检查它
  • 如果已被使用,增加计数器('02 “),追加登录并再次检查
  • 当非要求的登录发现,其返回给用户

我不要在此架构喜欢现在的问题是,它需要多个请求到MySQL数据库。有没有办法一次获得第一个无人认领的登录?也许与存储过程或聪明的SQL查询?

UPD:提供赏金

+18

两件小事:1。作为一个用户,我讨厌这个 - 我想要一个唯一的用户名,而不是我的用户名和一些数字。确保它是用户需要的东西,而不仅仅是您想要构建的功能。 2.确保您的解决方案不会引入任何安全问题,因为它允许恶意用户找出需要的名称,并且可能是吸取服务器资源的一种方式。 – Tom 2010-01-15 13:58:43

+0

用户名以'01010101'和'h4xx0r1337'这样的数字结尾如何?使用'99'前是否有固定范围的'00'? – BalusC 2010-01-15 14:03:45

+0

@Tom:客户的决定 @BalusC:固定范围就足够了 – 2010-01-15 15:53:50

回答

4

用户正则表达式找到所需要的匹配:

SELECT .. FROM users WHERE username REGEXP '^superman[0-9]{1,2}' 

这将返回所有的用户名在“supermanX”或“supermanXX”(一个或两个数字)的形式。

当你得到你的结果后,你可以很容易地找到下一个在线号码或丢失的号码。

欲了解更多信息,请阅读以下内容:

http://dev.mysql.com/doc/refman/5.0/en/pattern-matching.html

http://dev.mysql.com/doc/refman/5.0/en/regexp.html


编辑

假设表被称为 '用户' 和有关字段被称为“用户名”,可能的代码被剪切如下:

/** 
* Checks a given name exists at the users table 
* and returns possible alternatives 
* or an empty string if no alternatives can be found 
*/ 
function CheckUsername($name); 
    // sanitize 
    $query = sprintf("SELECT username FROM users 
      REGEXP '%s[0-9]{0,2}' ORDER BY username", 
      mysql_real_escape_string($name)); 

    $result = mysql_query($query); 

    // get all possible matches 
    $rows = array(); 
    while (list($match) = mysql_fetch_row($result)) { 
     $rows[] = $match; 
    } 

    if (count($rows) == 0) { 
     // no rows found, return the original name 
     return $name; 

    } else { 
     // found multiple rows 

     if ($rows[0] != $name) { 
      // first check if the original name exists 
      return $name; 

     } else { 
      // else go through each number until we find a good username 
      $count = 1; 
      while ($counter < count($rows) { 
       $test = sprintf("%s%02d", $name, $counter); 
       if ($rows[$counter] != $test) return $test; 
       $counter++; 
      } 
     } 
    } 

    // nothing found 
    return ''; 
} 

我希望它有帮助。

+1

正则表达式是你的朋友。 +1 – 2010-01-24 05:20:26

9

为什么不选择where login like 'superman%'和迭代在你的代码中的结果?

+14

,直到你的1.000.000用户网站上的某个人想要注册用户名'a'(并且即使你有最小用户名长度,它可以成为一个相当大的结果集) – 2010-01-15 13:53:35

+0

只需使用JavaScript上的验证长度(和其他限制)客户端,以避免这种情况。并且DEFINITELY从任何SQL字符串中清除字符串,以避免SQL注入攻击。 – 2010-01-15 13:59:11

+7

@Traveling Tech Guy:我认为在服务器上检查它会更安全 - javascript验证可以轻松绕过。 – 2010-01-15 14:04:40

3

可以,假设登录域正确索引(这应该是),这样做:

select login from usertable where login = 'Superman'; 

如果没有行返回,就大功告成了。否则,您将不得不检查其他可能性:

select login from usertable where login like 'Superman%' order by login; 

现在,只需找到具有最高数字后缀的变体并添加一个即可。

编辑:
一个查询到数据库只检查实际名称是快,但一个查询,以检查在一个大的数据库中的所有可能性将是缓慢的(而不是像匹配的,因为 - 它的快速,如果你被索引 - 而是下载所有这些行并处理它们)。

你会更好做1个查询核对姓名,则只能做查询,以检查所有名称时所需的名称不起作用。

您也可以缓存查询的结果,使他们可以在没有你不必返回到数据库的下一次有人拿起一个supermanesque名被重用。只要确保在向db添加类似的登录名时清除结果。

1

根据对该问题的评论,需要一个固定的范围00 - 99。你可以考虑在名字的最后两部分做一个SELECT MAX()

SELECT max(convert(substring(name, char_length(username)-1, 2), signed)) AS max 
    FROM user 
    WHERE name LIKE 'superman%' 

但这不是免费维护。如果有99 superman

这也不是免费的用户名已经结束与数字01010101h4xx0r1337的潜在冲突/冲突。如果已经有superman01superman02以及新的(和无知的)用户决定注册为superman88,因为他/她出生于1988年;任何下一个superman将得到superman89建议,留下superman02superman88之间的漏洞。

在这个特定的问题上很难给出“最好”的答案。该最安全的方式会是这样的:

if (find_user($username) != null) { 
    for ($i = 0; $user != null; $i++) { 
     $username = $username . $i; 
     $user = find_user($username); 
    } 
} 
// Now suggest $username. 

当然还有一个成本,但它不是震撼。又想一想,这会发生多少次?也许每天一次?或者一年一次,如果您的论坛平均每天只有一位新成员?

2

如果你愿意在数据库中存储一些状态...

当有人注册了一个用户名,把它贴在“可用”表,其中有两列,“BASE_NAME”(串)和“next_available”(整数)。如果有人注册以两位数字结尾的用户名,请查找基地(最后两位数字前面的部分),并将其插入“可用”或更新“next_available”。

当有人输入不可用的用户名时,您可以在“可用”表中查找并输出基数和next_available后缀。这可以在一个查询中完成。注意:如果有人注册了“superman93”,那么即使号码01到92可用,您也只能获得6个以上的用户名。

2

这里是我微不足道的解决方案:向用户表中添加一个varchar列(例如,称为username_string_part)以存储用户名的字符串部分,并在第二个int列(例如username_number_part)中存储数字部分。所以superman1在username_string_part列中被分成“超人”,在username_number_part中被分成“1”。如果您不期望大量重复的username_string_part条目,则还可以在两列或仅在username_string_part上创建索引。因此,在MySQL中,你创建的表是这样的)。

CREATE TABLE `users` (
    `id` int(11) NOT NULL auto_increment, 
    `username` varchar(25) NOT NULL default '', 
    `username_string_part` varchar(25) NOT NULL default '', 
    `username_number_part` int(11) NOT NULL default 0, 
    PRIMARY KEY (`id`), 
    KEY `ix_username_string_part` (`username_string_part`) 
) TYPE=MyISAM AUTO_INCREMENT=1; 

(请注意,用户名“超人”有一个默认的零username_number_part - 这很重要)

一旦你有几个条目,你的数据将是这个样子:

+----+-----------+----------------------+----------------------+ 
| id | username | username_string_part | username_number_part | 
+----+-----------+----------------------+----------------------+ 
| 1 | superman | superman    |     0 | 
| 2 | superman1 | superman    |     1 | 
| 3 | superman3 | superman    |     3 | 
+----+-----------+----------------------+----------------------+ 

然后,它的选择是没有在数据库中username_number_part价值“本身加一”的username_number_part最小值的情况。所以对于用户名“超人”:

select min(username_number_part) + 1 as min_number_available from users 
    where username_string_part = 'superman' and username_number_part not in 
    (select username_number_part - 1 from users where 
     username_string_part = 'superman'); 

返回值,min_number_available,是NULL如果这是该用户名的第一个实例 - 这样他们就可以拥有它 - 或者下一个空闲时隙的整数,否则。然后,您建议建议用户名为"superman" + min_number_available。你可以在查询中进行concat或不按你喜欢的方式。使用上面的示例数据,您将返回值“2”。

缺点:它会添加存储(列和索引),并且放慢插入速度。它也没有自然区分“超人001”和“超人01”。 (虽然如果您将前导零作为username_string_part的一部分处理,那么“superman001”将被拆分为“超人00”和“1”。)

上行:这是对索引列的单个查询。

毕竟,如果一个站点有太多的用户名重复,那么在多个数据库查询中执行for循环确实很糟糕,那么我会感到惊讶。

6

要求一个提示语这样的:

请另外提供一个提示短语,你想成为的情况下,您的用户名,你选择一个已经采取了别人的一部分。
例如,如果你的名字是约瑟夫,那么约瑟夫,约瑟夫或乔已经被采纳了。因此,您可以提供以下提示之一:

  1. 您的姓氏 - 例如,史密森 - 这将建议“joe.smithson”
  2. 你的居住城市 - 例如。湾区 - 这将建议“joseph_bayarea”
  3. 该帐户的目的 - 例如。开发商 - 这将建议“约瑟夫开发”
  4. 一种颜色 - 例如。蓝色 - 这将表明“bluejoe”
  5. 一个数字 - 这将是后缀,如“joe99”

另一种方式来得到这个提示信息会在注册表单中输入其他数据相结合。我不能马上想到的任何其他方便通用方案的猜测用户想要什么建议作为他的用户名。

尤其是因为您的网站提供的服务没有指定。

解决此问题的另一种方法是查看这些“智能”验证码后面的代码,即像Slashdot这样的网站生成。一些诙谐的开发;-)有一堆词语语义上链接到手头的主题,并使用这些短语进行验证码。

这智能/智能验证码的东西是有点谷歌集

编码恐怖偶尔也会显示这些智能catpchas。

玩弄这些类型的服务或获得一个良好的语义关联术语数据库。然后将这些条款与您要求用户提供的提示短语链接起来。

谷歌很容易做到这一点,因为“所有的搜索都属于Google”(TM)。
你有一个更容易的任务 - 你不必爬网,你不必提供搜索引擎结果或链接。所有你需要的是一个语义数据库。

你可能会得到一个是你看起来很难在线。
你可以从同义词/反义词开始。
IIRC,其中一个是wordnet,但我不知道许可证。所以,请查看它。


附加(可选,但不执行部分):
我建议,如果你做这样的好事,使开源。
这对其他人很有帮助,并为您提供出色的代表。
并且确保也针对不可避免的情况发布代码以防止自动登录,一些没有道德和大量业余时间的编码器将使用语义链接的开放式字词数据库来针对您的应用程序和其他所有生成注册请求!
机器人不断变得更聪明,更智能。
电子邮件验证是防止这种情况的一种保护措施 - 但这只有在电子邮件服务不能被破坏的情况下 - 如果它是一种新的电子邮件服务 - 它会一直出现。

因此,如果您要实现这个想法并将其作为开源发布,那么这是一个相当大的任务。那么你也必须保护它。

或者你可以保留它自己的网站。

+0

我实际上使用wordnet,我可以使用它。但现在的任务是实施已经选定的方案 - 有数字的方案。我正在寻求巧妙的方法来做到这一点。 虽然你的回答总体上不错,但我肯定会在开发其他应用程序时遵循你的建议:) – 2010-01-20 12:55:06

2

如果您可以更改数据库架构,则解决方案很简单。

将用户名分成两列:username和username_suffix(INTEGER)。

如果username_suffix为0,则不显示。即“超人”和“超人0”是等同的。

然后,您可以简单地

SELECT MAX(username_suffix)+1 WHERE username = 'superman' 

得到下一个可用的后缀。

或者,如果您无法更改数据库架构,请尝试按概率工作。附加一个随机的2位数字;如果与现有用户冲突,请附加一个随机的3位数字;如果发生冲突...

如果您不介意轻微地讨厌一小部分潜在用户,只是建议一个用户名,即用户提出的用户名,其中任何尾随数字被剥离并附加额外的随机数字,而不检查数据库首先可能会工作得很好:

例如。

superman not available, try superman39... (Try 2 extra digits first) 
superman39 not available, try superman491... (now try 1 extra digit each time) 
superman491 not available, try superman8972... (up to (say) 4 digits) 
superman9872 not available, try superman2758 

潜在的用户必须非常不幸地必须重试一次或两次以上。


由于某种原因,在我写这篇文章之前我没有看到@ Karl的解决方案。如果额外的分栏是最好的解决方案,那么他应该可以获得信贷 - 尽管我认为这很容易。然而,概率方法对我来说更有意义。

+0

我打算选择max(something)+1,但是我读了OP的“first无人认领的登录“,意思是他想要例如如果superman1和superman3被采用,则superman02返回,而选择max将返回超人4。当然,如果有人选择最大的int值作为后缀,它可能会中断...(如果有几十亿的超级玩家,这两者都会中断......) – 2010-01-20 08:55:19

4

这是我在这个旅途中:

SELECT `login` 
    FROM `usertable` 
WHERE `login` LIKE 'Superman%' 
ORDER BY `login` DESC 
LIMIT 1; 

如果查询没有返回结果$username = 'Superman',否则:

$username = 'Superman' . (strrev(intval(strrev($result['username']))) + 1); 

这应该做的伎俩,但我必须说,我不是你的用户名挑选方案的粉丝。


修改后的SQL查询,在klausbyskov的第一个评论的光:

SELECT `login` 
    FROM `usertable` 
WHERE `login` RLIKE '^Superman[0-9]*$' 
ORDER BY `login` DESC 
LIMIT 1; 
+0

那么,如果已经有一个用户叫做“SupermanWoman49”,但是没有用户叫做“超人”,而用户试图用“超人”来创建他自己会怎么样。他的用户名不会变成“SupermanWoman410”? – 2010-01-21 16:47:32

+0

@klausbyskov:它会变成超人的女人50,但很好的一点。我已经更新了我的答案。 – 2010-01-21 16:55:49

+2

我并不想挑剔这里,但想象数据库中唯一的用户名为“超人42”... – 2010-01-21 17:23:14

0

大多数答案都是正确的,但硬编码在SQL语句中所要求的用户名。

SELECT MAX(SUBSTR(user,LENGTH('{$request}')+1))+1 
FROM users 
WHERE username LIKE '{$request}%' 

将返回一个合适的后缀(NULL,如果用户名尚未使用)

C.

1

查询下面使用辅助表10条记录(数字“0”至“9 ')和一个交叉连接来创建一个字符串'00'到'99'的列表。这些值与用户选择的登录('超人')连接,并且测试的结果为您当前用户的表中的NOT IN。最终结果是一个可能的登录名列表('superman00'到'superman99'),它们目前没有被使用。您可以向用户显示其中的一些可供选择的内容。我在TSQL测试,应该很容易转换为MySQL的(我认为你必须与CONCAT('superman',T.i,U.i)更换'superman'+T.i+U.i):

--- prepare a digits table 
create table digits (i char(1)); 
insert into digits (i) values ('0') 
insert into digits (i) values ('1') 
insert into digits (i) values ('2') 
insert into digits (i) values ('3') 
insert into digits (i) values ('4') 
insert into digits (i) values ('5') 
insert into digits (i) values ('6') 
insert into digits (i) values ('7') 
insert into digits (i) values ('8') 
insert into digits (i) values ('9') 

--- This query returns all 'superman00' to 'superman99' records currently not used 

SELECT 'superman'+T.i+U.i AS suggestedlogin 
    FROM digits T cross join digits U 
    WHERE 'superman'+T.i+U.i NOT IN (
    SELECT login FROM usertable 
) 

(十字从http://www.tek-tips.com/viewthread.cfm?qid=755853加入主意)

+1

纠正我,如果我错了,但我认为存储数字表是相当多的矫枉过正。我知道存储现在便宜一天,但仍然 – 2010-01-24 05:25:34

+0

@伊丽莎白Buckwalter - 我不喜欢辅助表,我从来没有需要在生产中使用它们,但它是一个公认的解决方案。查阅Joe Celko撰写的这篇文章:http://intelligent-enterprise.informationweek.com/showArticle.jhtml?articleID=202802386。另外,SP可以使用临时表。 – 2010-01-24 05:55:07