2

我正在使用 php 开发记录管理应用程序。我设计了一个名为 records 的表,其中包含字段id, recordname, recordvalue, recordid。例如,如果一条记录包含 3 个数据firstname, lastname, gender. 表中插入了 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 轻松实现这一点?

提前致谢。

4

7 回答 7

3

我认为你应该修理你的桌子,它的设计非常糟糕。应该是这样的

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

然后你可以使用 UNIQUE INDEX (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' 
于 2013-04-17T11:57:23.990 回答
0

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

eav_hell(entity*,attribute*,value) 

* = (component of) PRIMRY KEY

并为可怜的丢弃数据类型留心。

于 2013-04-17T12:09:45.593 回答
0

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

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

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

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

我怎样才能用 PHP 轻松实现这一点?

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

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

于 2013-04-17T11:54:32.433 回答
0

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

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。

于 2013-04-17T12:01:06.820 回答
0

你将需要这样的东西:

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,因此该组合已经存在)

于 2013-04-17T12:01:57.737 回答
0

谢谢大家的付出。无论如何,我找到了解决问题的方法。它很棘手,我不知道它的效率如何。所以我在这里发布它以征求专家的意见。谢谢。

我的问题是-我有一个 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如果我得到一个非空数组,我确定在单个记录下提交的值有一个或多个重复。

这个过程还帮助我告诉用户存在一些部分匹配的值。它对我很有效。

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

于 2013-04-18T08:05:02.353 回答
0

您可以尝试以下查询,如果返回答案,那么您已经有类似的数据。如果不是,它将返回 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'
于 2013-04-17T12:06:15.797 回答