【问题标题】:how to check a value in multiple rows in mysql table associated with a unique data in each row如何检查与每行中唯一数据关联的mysql表中多行中的值
【发布时间】:2013-04-10 03:45:12
【问题描述】:

我正在使用 php 开发一个记录管理应用程序。我设计了一个名为记录的表,其中包含字段idrecordnamerecordvaluerecordid。例如,如果一条记录由 3 个数据组成:firstnamelastnamegender。 3 个数据(John Doe Male、Jane Doe Female 和 Sherlcok Holmes Male)插入到下表中-

----+-------------+--------------+-----------
id  | recordname  | recordvalue  | recordid
----+-------------+--------------+-----------
1   | firstname   | John         | 001
2   | lastname    | Doe          | 001
3   | gender      | Male         | 001
4   | firstname   | Jane         | 002  
5   | lastname    | Doe          | 002
6   | gender      | Female       | 002
7   | firstname   | Sherlock     | 003
8   | lastname    | Holmes       | 003
9   | gender      | Male         | 003

----+-------------+--------------+-----------

我的问题是当我要在表中插入新记录时,我需要检查记录是否有任何重复。这意味着可以随时复制不同记录的任何字段,但不能复制具有相同记录值的所有字段。从前面的示例记录中,具有 John Doe Female、Jane Doe Male、Sherlock Doe Male 等值的记录都可以,但不希望再次插入 John Doe Male,它会给出错误。

如何使用 PHP 轻松实现这一点?

提前致谢。

【问题讨论】:

  • 我无法理解您的数据库结构,为什么您要存储不同的名字、姓氏和性别记录
  • 您在表结构中使用这种方法(通常称为实体属性值;简称 EAV)是否有原因?与更传统的规范化数据结构相比,它通常不太适合与关系数据库一起使用。
  • @MarkBannister 我不明白他为什么使用这种结构
  • @maksbd19 先学习如何规范化表格
  • @YadavChetan:在没有更多信息的情况下,我也不能,尽管在某些情况下这种方法是合适的——请参阅en.wikipedia.org/wiki/…

标签: php mysql


【解决方案1】:

我认为你应该修一下你的桌子,它的设计真的很糟糕。应该是这样的

table
--------------------------------------------
record_id | firstname | lastname | gender

然后你可以使用唯一索引(名字、姓氏、性别)来控制它,如果你想只在你的数据库中保持它。

[编辑]

由于您无法更改数据库设计并且它使用 EAV 结构,您可以使用下面的查询来实现您要查找的内容。如果有或没有基于您的条件的记录,此查询将返回。

SELECT a.recordvalue, b.recordvalue, c.recordvalue
FROM rec_eav a
INNER JOIN rec_eav b ON (b.recordid = a.recordid AND b.recordname = 'lastname' )
INNER JOIN rec_eav c ON (c.recordid = a.recordid AND c.recordname = 'gender' )
WHERE a.recordname = 'firstname' 
    AND a.recordvalue = 'John'
    AND b.recordvalue = 'Doe'
    AND c.recordvalue = 'Male' 

【讨论】:

  • 感谢@medina,但我为每个记录名称 recordid 都有一个唯一的 ID,我用它来跟踪单个记录集中的字段。我需要检查的是,当我提交表单时,从表单提交的值与数据库中已经存在的另一条记录不同,如果找到任何匹配项,它将返回错误。再次感谢。
  • 抱歉再次发表评论。我真的需要设计一个 EAV 模型,因为我不知道属性。它们仅由管理员设置。我确实理解您的建议,但这不是我想要的。我不能改变我的数据库设计。无论如何,谢谢。
  • 嘿伙计,我理解您对 EAV 的痛苦。我每天都与 Magento 一起工作,我非常了解它的情况。无论如何,请查看我在评论中完成的版本。 ;-)
【解决方案2】:

我的问题是当我要在表中插入一条新记录时,我需要检查记录是否有任何重复。

您的“记录”与此处的普通数据库记录(= 一行)不同。

这意味着可以随时复制不同记录的任何字段,但不能复制具有相同值的记录的所有字段。从前面的示例记录中,具有 John Doe Female、Jane Doe Male、Sherlock Doe Male 等值的记录都可以,但不希望再次插入 John Doe Male,它会给出错误。

因此,在您的情况下,多条记录的组合必须是唯一的。

如何使用 PHP 轻松实现这一点?

正如 Yadav 在他的回答中所建议的那样,您可以先 SELECT 并查看是否获得了已插入数据的结果。但这可能会导致 TOCTTOU 问题——为避免这种情况,您必须将 SELECT 和 INSERT 封装到事务中。

另一种方法是使用 INSERT ... SELECT 语句。 SELECT 语句必须以这样一种方式制定,即它只返回要插入的值,如果它确实没有已经找到它们 - 计算匹配的记录并使用 HAVING 只过滤那些提供正确数量的记录(在您的示例中为三个)会这样做。

【讨论】:

  • 感谢您的解释性回答。你能举个例子来说明你的想法吗?
【解决方案3】:

你需要这样的东西:

SELECT a.recordid
FROM test a
JOIN test b ON ( b.recordname = 'firstname' AND b.recordvalue = 'John' AND b.recordid = a.recordid)
JOIN test c ON ( c.recordname = 'lastname' AND c.recordvalue = 'Doe' AND c.recordid = a.recordid)
WHERE (a.recordname = 'gender' AND a.recordvalue = 'female')

如果结果为空,那么您可以继续(就像在我的示例中,如果您将性别值更改为男性,它将返回一个记录 ID,因此该组合已经存在)

【讨论】:

  • 谢谢@Xavjer 我想我需要你给的东西。你能解释一下吗?谢谢。
  • 这并不难,只需为您拥有的每个记录名/值对创建一个连接并将其与记录ID 连接,最后一个记录名/值对可以在 WHERE 子句中(您甚至可以创建一个more join 并使用 no where 语句)。如果组合存在,它会返回重复记录的recordid
【解决方案4】:

如果您要使用 EAV 模型来存储数据,请考虑如下结构:

eav_hell(entity*,attribute*,value) 

* = (component of) PRIMRY KEY

并为那些被丢弃的可怜数据类型留个心眼。

【讨论】:

    【解决方案5】:

    运行类似于以下内容的查询:

    select count(*) countmatch
    from datatable
    where concat(recordname,':',recordvalue) in
          ('firstname:John', 'lastname:Doe', 'gender:Male')
    group by recordid
    order by countmatch desc
    limit 1
    

    -如果返回的countmatch值为3,则拒绝新的John Doe Male记录,否则将其添加到表中。

    编辑:如果名字、姓氏和性别一起唯一标识一条记录,但其他属性可能稀疏和/或任意填充,那么您可能需要考虑混合模式 - 可能是这样的:

    KeyTable
    --------
    firstname
    lastname
    gender
    recordid
    

    - 在名字、姓氏和性别的组合上具有唯一索引,在 recordid 上具有单独的唯一索引。

    DataTable
    ---------
    

    - 和现在一样,在 recordid 上有一个外键。请注意,名字、姓氏和性别属性应该存储在此表中。

    当尝试添加名字、姓氏和性别的“新”组合时,请尝试将它们添加到 KeyTable - 如果插入被 DBMS 拒绝,则意味着该记录已经存在。

    在这种情况下,存储在 DataTable 上的记录名称/记录值组合应始终包含一个记录 ID,该记录 ID 链接到 KeyTable 上的有效记录 ID。

    【讨论】:

    • 这是非常低效的。它在每个 INSERT 之前需要一个额外的 SELECT,它需要在检查数据之前对每一行进行插值,它确保您实际上不能使用任何索引来加速查询,并且它破坏了这种模式的一个好处 - 其中表示将属性作为行而不是列提供了添加多个任意属性的可能性。
    • @symcbean:我认为你是反对者?基于 EAV 的 SQL DBMS 系统中的任何复合键检查都需要在每次 INSERT 之前进行 SELECT,与其他处理相比,行的插值(我假设您的意思是字段的串联)是微不足道的,并且多个(recordname='firstname' and recordvalue='John') or ... 条件也意味着您不能使用索引。这些是为什么将 EAV 模式与 RDBMS 一起使用通常是一个坏主意的一些原因。
    • @symcbean:此外,此查询不会破坏添加多个任意属性的能力。
    • (我的编辑中建议的混合模式会明确限制添加 key 属性的能力,但不会限制添加 non-key 属性。)跨度>
    【解决方案6】:

    您可以尝试以下查询,如果返回答案,那么您已经有类似的数据。如果不是,它将返回 0 行:

    select recordid from records 
    where recordvalue in ('John', 'Doe', 'Male')
    group by recordid
    having count(recordid) = 3
    

    编辑:如@Xavjer 所解释,上述查询可能会给出超过 1 个结果,试试这个:

    select * from 
    (
    select 
    max(case when recordname="firstname" then recordvalue end) as firstname,
    max(case when recordname="lastname" then recordvalue end) as lastname,
    max(case when recordname="gender" then recordvalue end) as gender,
    recordid
    from records
    group by recordid
    ) as record
    where firstname='doe'
    and lastname='john'
    and gender='male'
    

    【讨论】:

    • 我首先认为这将是一个比我更好的解决方案,但是如果有一个字段可以具有相同的值,那么你会遇到问题(就像我们有一个叫 Nelson Quinn 的人,还有另一个叫奎因·纳尔逊)
    • 你是对的。我没有猜到这种情况。但即便如此,我也不想在这种情况下使用JOINS,因为记录的数量总是很大的。我已经编辑了我的答案并包含了另一种方式。但我同意@medina,这需要更好的架构设计。
    【解决方案7】:

    感谢大家的付出。无论如何,我找到了解决问题的方法。它很棘手,我不知道它的效率如何。所以我把它贴在这里给专家的cmets。谢谢。

    我的问题是 - 我有一个 EAV 表,其中单个实体具有多个属性和相应的值。我需要检查是否有人提交了包含某些记录值的表单,所有这些值都已作为另一条记录在数据库中。例如。如果 John Doe Male 已经在 recordid001 下的表中,则用户不能将 John Doe Male 保存为另一条记录,而是会给用户一条消息“记录已存在!”。

    我执行了一些查询 -

    foreach($submitted_values as $name => $value){
        $r = mysql_query("SELECT DISTINCT `recordid` FROM `records` WHERE `recordname` = '$name' AND `recordvalue` = '$value'");                
        while($row  = mysql_fetch_assoc($r)){
            $assoc[$name][] = $row['recordid']; 
        }
    }
    

    所以在$assoc 中,我得到了一个以字段名称为索引的二维数组,并与每个匹配的recordid 数组相关联。

    然后我检查$assoc 数组中是否有任何匹配的值,如果我得到一个非空数组,我确定在单个记录下提交的值有一个或多个重复。

    这个过程还帮助我告诉用户存在一些部分匹配的值。它非常适合我。

    再次感谢大家浪费了宝贵的时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-25
      • 2014-09-15
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      相关资源
      最近更新 更多