【问题标题】:How to check if a given data exists in multiple tables (all of which has the same column)?如何检查给定数据是否存在于多个表中(所有表都具有相同的列)?
【发布时间】:2011-08-29 16:08:54
【问题描述】:

我有 3 个表,每个表都包含一个名为 username 的列。在注册部分,我需要检查请求的用户名是否是新的且唯一的。

在我继续之前,我需要一条 SQL 来告诉我该用户是否存在于这些表中的任何一个中。我试过了:

SELECT tbl1.username, tbl2.username, tbl3.username
FROM tbl1,tbl2,tbl3
WHERE tbl1.username = {$username}
   OR tbl2.username = {$username}
   OR tbl3.username ={$username}

这是要走的路吗?

【问题讨论】:

  • 你有三个表有相同的列??我假设您确实知道规范化,这是故意的吗?
  • 我是一个完全没有数据库编程的菜鸟 :)
  • 那么我建议您在阅读了高达 3NF 的规范化之后回到您的数据库设计。像用户名这样的东西应该只存储一次并与 ID 相关联,然后由其他表通过外键引用。那么你当前的问题,以及未来可能的许多其他问题,会简单得多。此外,您还应该考虑如果用户名存在于 >0 但
  • @Brian Patterson 有点苛刻,不是吗?我从来没有遇到过一个不“在工作中学习”的开发人员。诚然,海报处于最极端的位置,但尝试某人并不是鼓励改进的最佳方式
  • @Ted:将邀请创建为普通用户记录,但在表中添加确认标志。您可以定期删除超过 X 天的未确认记录,以释放这些用户名供其他人使用。

标签: mysql sql


【解决方案1】:
select 1 
from (
    select username as username from tbl1
    union all
    select username from tbl2
    union all
    select username from tbl3
) a
where username = 'someuser'

【讨论】:

  • 很好'给一个男人一个鱼的答案',但这并不能解决根本问题。
  • 嗨 RedFilter,这给了我一个错误我在 phpMyAdmin 中运行它,你能检查一下吗?谢谢
  • 不,它说“操作'UNION'的排序规则的非法混合”
  • 听起来每个用户名列的排序规则都不相同。
  • 你是对的。我有 utf8_unicode_ci 而不是 utf8_general_ci,谢谢!
【解决方案2】:

如果您真的只想知道用户是否存在:

最快的方法是存在查询:

select 
NOT EXISTS (select username from a where username = {$username}) AND 
NOT EXISTS (select username from b where username = {$username}) AND 
NOT EXISTS (select username from c where username = {$username});

如果您的username 列在每个表中都标记为Unique,这应该是您能够执行此操作的最有效查询,并且此将优于规范化用户名表 em> 在内存使用方面,以及几乎 任何 其他关心 username 和另一列的查询,因为没有过多的连接。如果您曾被要求加速组织的数据库,我可以向您保证,过度规范化是一场噩梦。关于您在此线程中收到的关于规范化的建议,请小心。它非常适合限制空间或限制您必须更新数据的位置数量,但您必须权衡维护和速度开销。对本页提供给您的建议持保留态度。

习惯于对您的查询运行查询分析器,如果没有其他原因,只是为了养成在编写查询时学习选择的后果的习惯——至少在您掌握海腿之前。

如果您想稍后插入用户:

如果您这样做是为了最终将用户添加到数据库中,这里有一个更好的方法,值得学习。尝试立即插入该值。之后检查是否成功。这样,其他数据库调用就没有空间在您检查的时间和插入数据库的时间之间插入记录。例如,在 MySQL 中,您可以这样做:

INSERT INTO {$table} (`username`, ... )
  SELECT {$username} as `username`, ... FROM DUAL 
   WHERE 
     NOT EXISTS (select username from a where username = {$username}) AND 
     NOT EXISTS (select username from b where username = {$username}) AND 
     NOT EXISTS (select username from c where username = {$username});

我见过的所有数据库 API 以及所有 SQL 实现都会为您提供一种方法来发现插入了多少行。如果为1,则用户名不存在,插入成功。在这种情况下,我不知道你的方言,所以我选择了 MySQL,它提供了一个DUAL 表专门用于返回未绑定到表的结果,但老实说,有很多方法可以对此进行皮肤处理cat,不管是放在事务还是存储过程中,还是严格限制可以访问这些表的进程和过程。

更新——如何处理未完成注册过程的用户

正如@RedFilter 指出的那样,如果注册是通过多个步骤完成的——保留用户名、填写详细信息、可能回复电子邮件确认,那么您至少需要添加一列来标记该用户(带有时间戳,而不是布尔值),以便您可以在一段时间后定期删除用户,但我建议创建一个 ToBePurged 表并向其中添加新用户以及时间戳。当确认通过时,您从该表中删除该用户。您将定期检查此表中的所有条目,然后再与当前时间相差一些,然后将它们从最初添加的表中删除。我背后的理念是更清楚地定义表的职责并保持您正在使用的记录数量非常精简。我们当然不想过度设计我们的解决方案,但如果您养成良好架构实践的习惯,这些设计将与效率较低的同类设计一样自然地流出。

【讨论】:

  • 如果在“a”中没有相应的行,(内部)连接是否不会过滤掉“b”和“c”表中的用户名? (可能不是所有的用户名都在'a'、'b'和'c'中,否则就没有必要检查所有表中是否存在)
  • 如果用户名在所有三个表中都存在,使用内连接只会返回一行。这似乎不是 OP 想要的。如果名称只出现在其中一张表中,则内连接将始终为空,因此您需要进行三个独立的存在检查。但是,我在插入查询中确实犯了一个错误,由于某种原因,它确实使用了内部连接。我随后修复了查询,所以如果您指的是那个,那么我的不好的,好的捕获 - 我的意思是从第一个查询中剪切并粘贴存在检查。
  • 是的,我只是指您的“插入”声明,正如您所说,现在已更正;我很抱歉最初没有把它说得更清楚 - 当谈到清晰时,我在评论中的反问中错过了“反之亦然”,这并没有帮助! :)
【解决方案3】:

没有。两个进程可以同时运行您的测试,并且都将报告没有用户,然后都可以插入相同的用户。

听起来您需要一个表来保存所有具有唯一索引的用户以防止重复。此主表可以使用用户 ID 而不是用户名链接到“子表”。

【讨论】:

    【解决方案4】:

    考虑到排序规则,如果您不想处理排序规则不匹配,您可以这样做:

    select sum(usercount) as usercount
    from (
        select count(*) as usercount from tbl1 where username = 'someuser'
        union all
        select count(*) as usercount from tbl2 where username = 'someuser'
        union all
        select count(*) as usercount from tbl3 where username = 'someuser'
    ) as usercounts
    

    如果您得到 0,则没有具有该用户名的用户,如果您得到更高的用户名,则存在。

    注意:根据您的插入方式,理论上您可能会因为竞争条件而获得多个具有相同用户名的用户(请参阅其他有关规范化和唯一键的 cmets)。

    【讨论】:

      【解决方案5】:

      1- 你需要规范化你的表格

      见:http://databases.about.com/od/specificproducts/a/normalization.htm

      2- 不要使用隐式 SQL '89 连接。

      改掉习惯并使用显式连接

      SELECT a.field1, b.field2, c.field3
      FROM a
      INNER JOIN b ON (a.id = b.a_id)  -- JOIN criteria go here
      INNER JOIN c ON (b.id = c.b_id)  -- and here, nice and explicit.
      WHERE ... -- filter criteria go here.
      

      【讨论】:

      • 但是内部连接应该匹配不是吗?我需要检查其中一个是否存在该值。如果有的话,它应该只有一个
      • 内连接不需要匹配,尽管它们通常会匹配。
      • @ted,无论如何,首先你需要规范化,只有在那之后连接才变得相关。
      • 他根本不应该对这些表进行规范化,除非除了用户名本身之外,这些表之间有很大的重叠。
      【解决方案6】:

      使用您当前的设置,RedFilter 的答案应该可以正常工作。我认为值得注意的是,您的数据库中不应有冗余或分散的数据。

      您应该只有一个位置来存储任何特定数据 - 因此,在您的情况下,您应该有一个包含用户名和这些用户名的主键标识符的表,而不是在 3 个不同的表中使用用户名。然后,您的其他 3 个表应该外键引用用户名表。您将能够使用此布局构建更简单、更高效的查询。您通过在不同位置复制数据打开了一罐蠕虫。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-07
        • 2019-05-16
        • 2022-12-13
        • 1970-01-01
        • 2010-12-31
        • 2019-03-22
        • 1970-01-01
        相关资源
        最近更新 更多