MySQL - 确保跨多行的唯一值的最佳方法

时间:2012-03-16 00:20:02

标签: mysql sql join unique

我有3张桌子:

Molecule:
  id

Atom:
  id

MoleculeAtom: # Composite primary key
  molecule_id
  atom_id

我的目标是确保不再重复构成分子的原子组合。例如,水分子,我会在MoleculeAtom表中存储两行;对于氢原子为1行,对于氧原子为1行。正如您所看到的,我需要确保没有其他分子只有氢气和氧气,即使可能还有其他分子包含氢和氧。

此时我有一个查询,它确定哪些分子包含氢或氧,并且在MoleculeAtom表中只有2个原子。

SELECT
  m.id, m.name, (SELECT count(*) from molecule_atom where molecule_id = m.id group by molecule_id) as atomCount
FROM
  molecule AS m
INNER JOIN
  molecule_atom AS ma ON ma.molecule_id = m.id
WHERE
  ma.atom_id IN (1,2)
HAVING atomCount = 2;

返回(示范片段):

+----+----------------------------+-----------+
| id | name                       | atomCount |
+----+----------------------------+-----------+
| 53 | Carbon Dioxide             |         2 |
| 56 | Carbon Monoxide            |         2 |
+----+----------------------------+-----------+

(我知道,CO和CO2具有相同的原子,数量不同,但不同意,因为我在同一张表中跟踪数量作为另一列。)

截至目前,我正在提取上述结果并通过PHP检查其atom_ids,这意味着我必须为每个分子发出一个单独的查询,这似乎效率低下,所以我想看看是否可以使用严格的SQL。

请原谅任何可能与化学有关的错误,自chem101以来已经很长时间了。

4 个答案:

答案 0 :(得分:1)

您要求的是表级约束,这些在MySQL中不可用。在SQL-92标准中,有ASSERTION,实际上更为通用(跨越多个表的约束)。请参阅此问题中的asnwers:Why don't DBMS's support ASSERTION以获取有关此类功能的一些产品(MS-Access)的详细信息和信息。

在MySQL中,您可以尝试使用触发器来模仿这样的约束。


更新

Firebird documentation表示它允许CHECK约束中的子查询。

答案 1 :(得分:0)

唯一索引可能对molecular_atom表有帮助。这样可以防止该级别的重复。您仍然需要通过SQL语句进行一些检查。根据列表大小的另一个选择是将其加载到哈希表的内存中,然后从那里运行检查。

答案 2 :(得分:0)

这里的想法是找到原子列表不相同的分子对:

select m1.molecule_id as m1id, m2.molecule_id as m2id
from molecule_atom as m1, molecule_atom as m2,
    (select atom_id from molecule_atom as m where m.molecule_id=m1id) as m1a,
    (select atom_id from molecule_atom as m where m.molecule_id=m2id) as m2a,
where m1id < m2id and (((m1a - m2a) is not null) or ((m2a - m1a) is not null))

答案 3 :(得分:0)

正如ypercube所提到的,MySQL不支持断言,所以我写了一个查询来查找所有分子中至少有一个原子属于我试图创建的新分子,并且具有相同数量的原子。在查询匹配之后,应用程序逐步遍历每个分子并确定它们是否具有与新分子相同的精确原子。查询看起来像这样(假设我正在尝试创建一个包含2个原子的新分子):

SELECT 
    m.id,
    m.name,
    (SELECT GROUP_CONCAT(ma.atom_id) FROM molecule_atom AS ma WHERE ma.molecule_id = m.id GROUP BY ma.molecule_id HAVING (SELECT COUNT(ma.atom_id)) = 2) AS atoms
FROM
    molecule AS m
INNER JOIN
    molecule_atom AS mas ON mas.molecule_id = m.id
WHERE 
    mas.atom_id IN (1,2)

然后在代码(PHP)中我做:

foreach ($molecules as $molecule) {

    if (isset($molecule['atoms'])) {

        $diff = array_diff($newAtomIds, explode(',', $molecule['atoms']));

        // If there is no diff, then we have a match
        if (count($diff) === 0) {
            return $molecule['name'];
        }
    }
}

感谢大家的回复。