使用NOT IN()mysql与缺少唯一行或主键的表

时间:2014-06-17 00:12:50

标签: mysql sql primary-key distinct

我有两个表:AP和表AP supervisory visits,分别有一对多关系。 AP supervisory visits没有主键或唯一索引(因此可能有点难以使用)和函数" IN()"这是不容易的。

AP

+------------------------------+-------------+------+-----+---------+-------+
| Field                        | Type        | Null | Key | Default | Extra |
+------------------------------+-------------+------+-----+---------+-------+
| HID                          | varchar(50) | NO   | PRI | NULL    |       |
| yr                           | varchar(50) | NO   | PRI | NULL    |       |
| mo                           | varchar(50) | NO   | PRI | NULL    |       |

AP supervisory visits

+------------------+-------------+------+-----+---------+-------+
| Field            | Type        | Null | Key | Default | Extra |
+------------------+-------------+------+-----+---------+-------+
| HID              | varchar(50) | YES  | MUL | NULL    |       |
| yr               | varchar(50) | YES  |     | NULL    |       |
| mo               | varchar(50) | YES  |     | NULL    |       |
| exported         | datetime    | YES  |     | NULL    |       |
| visitor name     | varchar(50) | YES  |     | NULL    |       |
| title            | varchar(50) | YES  |     | NULL    |       |
| reason for visit | varchar(50) | YES  |     | NULL    |       |
| number of visits | int(11)     | YES  | MUL | 0       |       |
+------------------+-------------+------+-----+---------+-------+

我在AP中有大约8000条记录,在AP supervisory visits中有1700条唯一记录(使用HID,年,月)。我希望AP表中没有'孩子的所有记录'在AP supervisory visits表中。使用关于Stakeoverflow的其他文章我得出结论我应该使用" NOT IN()" 。这适用于我的几个具有相似关系的表,但不是这次(注意最后一个查询是反向的,它缺少NOT):

mysql> SELECT HID, yr, mo FROM AP
    -> WHERE (HID, yr, mo) NOT IN(SELECT HID, yr, mo FROM `AP supervisory visits`);
Empty set (0.34 sec)

mysql> SELECT HID, yr, mo FROM AP
    -> WHERE (HID, yr, mo) NOT IN(SELECT DISTINCT HID, yr, mo FROM `AP supervisory visits`);
Empty set (0.47 sec)

mysql> SELECT HID, yr, mo FROM AP
    -> WHERE (HID, yr, mo) NOT IN(SELECT DISTINCT * FROM (SELECT DISTINCT HID, yr, mo FROM `AP supervisory visits`) AS temp);
Empty set (3.04 sec)

mysql> SELECT HID, yr, mo FROM AP
    -> WHERE (HID, yr, mo) IN(SELECT DISTINCT HID, yr, mo FROM `AP supervisory visits`) LIMIT 5;
+-----+------+----+
| HID | yr   | mo |
+-----+------+----+
| 109 | 2011 | 03 |
| 109 | 2012 | 05 |
| 109 | 2012 | 06 |
| 110 | 2010 | 11 |
| 110 | 2010 | 12 |
+-----+------+----+
5 rows in set (0.00 sec)

我创建了一个临时表,其中包含HID,yr,mo,的所有不同组合,但我宁愿不使用临时表。上面的第三个查询应该是在内存中创建一个具有不同值的临时表(我知道这很难看)但似乎并非如此。

我的临时解决方案':

mysql> CREATE TABLE myTempAPSup SELECT DISTINCT HID, yr, mo FROM `AP supervisory visits`;
mysql> ALTER TABLE myTempAPSup ADD PRIMARY KEY(HID, yr, mo);
mysql> SELECT HID, yr, mo FROM AP
    -> WHERE (HID, yr, mo) NOT IN (SELECT HID, yr, mo FROM myTempAPSup) LIMIT 5;
+-----+------+----+
| HID | yr   | mo |
+-----+------+----+
| 109 | 2010 | 01 |
| 109 | 2012 | 01 |
| 109 | 2012 | 02 |
| 109 | 2012 | 03 |
| 109 | 2012 | 04 |
+-----+------+----+
5 rows in set (0.00 sec)

有没有办法让没有“孩子”的行获得'从这么糟糕的桌子?我认为我的麻烦源于缺乏独特/主键,但是我错过了其他一些语法错误'?

3 个答案:

答案 0 :(得分:2)

如果你来自Oracle背景,左连接的想法,然后检查null可能看起来很奇怪。这个解决方案虽然很快,但是你并没有扫描联接表中的每个记录。另一种方法是使用相关子查询和not exists子句。

SELECT a.HID
     , a.yr
     , a.mo 
  FROM AP a
 WHERE NOT EXISTS (
       SELECT v.HID
         FROM `AP supervisory visits` v
        WHERE v.HID = a.HID
          AND v.yr  = a.yr
          AND v.mo  = a.mo
 )

答案 1 :(得分:1)

您可以使用LEFT JOIN并检查是否有子记录,如果不存在则为空。

select a.HID, a.yr, a.mo
from AP a
left join `AP supervisory visits` b on a.HID = b.HID and a.yr = b.yr and a.mo = b.mo
where b.yr is null;

答案 2 :(得分:1)

以下是获取AP中没有“匹配”行的AP supervisory visits行的一种方法:

SELECT a.HID
     , a.yr
     , a.mo 
  FROM AP a
  LEFT
  JOIN `AP supervisory visits` v
    ON v.HID = a.HID
   AND v.yr  = a.yr
   AND v.mo  = a.mo
 WHERE v.HID IS NULL

这是一个左外连接,用于从AP获取所有行,以及来自AP supervisory visits的匹配行。 “技巧”是WHERE子句中的谓词...我们排除了所有匹配的行。 (如果匹配,我们检查一个我们知道非NULL的列;如果没有匹配,列的唯一方法就是NULL。