Mysql SQL对一对多关系中不匹配的记录进行计数

Mysql SQL对一对多关系中不匹配的记录进行计数,mysql,Mysql,我有两个MySQL表: 测量(日期、位置、公里) 主键:日期+位置 (每次调查一项记录) 样本(日期、位置、物种)(每个调查日期和位置零个或多个记录) 我想找出调查的数量和调查的公里数,其中样本表中没有特定物种的记录。换句话说,没有发现特定物种的调查数量 调查总数为: select count(date) as surveys, sum(kilometers) as KM_surveyed from surveys; +---------+-------------+ | surveys

我有两个MySQL表:

测量(日期、位置、公里) 主键:日期+位置 (每次调查一项记录)

样本(日期、位置、物种)(每个调查日期和位置零个或多个记录)

我想找出调查的数量和调查的公里数,其中样本表中没有特定物种的记录。换句话说,没有发现特定物种的调查数量

调查总数为:

select count(date) as surveys, sum(kilometers) as KM_surveyed 
from surveys;

 +---------+-------------+
 | surveys | KM_surveyed |
 +---------+-------------+
 |   20141 |    40673.59 |
 +---------+-------------+
select count(*) from specimens where species = 'HASE';

 +-----------+
 | count(*)  |
 +-----------+
 |       662 |
 +-----------+
很容易找到未发现样本的调查数量:

select count(s.date) as surveys, sum(s.kilometers) as KM_surveyed 
from surveys=s left join specimens=p 
on (s.date=p.date and s.location=p.location)
where p.date is null;

 +---------+-------------+
 | surveys | KM_surveyed |
 +---------+-------------+
 |    8820 |    15848.26 |
 +---------+-------------+
样本中的记录总数为:

select count(*) from specimens;

+-----------+
|  count(*) |
+-----------+
|     51566 |
+-----------+ 
所有调查中发现的港口封条(HASE)的正确数量为:

select count(date) as surveys, sum(kilometers) as KM_surveyed 
from surveys;

 +---------+-------------+
 | surveys | KM_surveyed |
 +---------+-------------+
 |   20141 |    40673.59 |
 +---------+-------------+
select count(*) from specimens where species = 'HASE';

 +-----------+
 | count(*)  |
 +-----------+
 |       662 |
 +-----------+
找到发现海豹(HASE)的调查数量并不容易。
由于每个调查的样本表通常包含多条记录,因此此查询返回的不是调查数量,而是找到的样本数量:

select count(s.date), sum(s.kilometers) 
from surveys=s 
left join specimens=p on (s.date=p.date and s.location=p.location) 
where p.species = 'HASE';

 +---------+-------------+
 | surveys | KM_surveyed |
 +---------+-------------+
 |     662 |     2030.70 |  WRONG! that is number of specimens not surveys 
 +---------+-------------+
找到没有发现海豹(HASE)的调查数量也不容易。此查询返回的不是调查数量,而是发现的不是海豹的样本数量:

select count(s.date), sum(s.kilometers) 
from surveys=s 
left join specimens=p on (s.date=p.date and s.location=p.location) 
where p.species <> 'HASE' or p.date is null;`

 +---------+-------------+
 | surveys | KM_surveyed |
 +---------+-------------+
 |   50904 |   151310.49 | 
 +---------+-------------+
选择计数(s.date)、总和(s.km)
来自调查=s
左连接试样=p开启(s.date=p.date,s.location=p.location)
其中p.物种“HASE”或p.日期为空`
+---------+-------------+
|测量| KM|测量|
+---------+-------------+
|   50904 |   151310.49 | 
+---------+-------------+
错了!50904=非相位试样的数量


如何构造查询以正确计算发现港口封条的调查数量以及未发现封条时的调查数量?

当您进行
左连接时
要查找不匹配的行,应将不应匹配的标准放入
ON
子句中,而不是
where
子句中

SELECT COUNT(*), SUM(s.kilometers)
FROM surveys AS s
LEFT JOIN specimens AS p ON s.date = p.date and s.location = p.location
    AND p.species = 'HASE'
WHERE p.date IS NULL

在执行
左联接
以查找不匹配的行时,应将不应匹配的条件放入
ON
子句中,而不是
WHERE
子句中

SELECT COUNT(*), SUM(s.kilometers)
FROM surveys AS s
LEFT JOIN specimens AS p ON s.date = p.date and s.location = p.location
    AND p.species = 'HASE'
WHERE p.date IS NULL

您可以在WHERE子句中使用
EXISTS
/
NOT EXISTS
子查询

样本
表中找到
HASE
的调查:

select count(*), sum(s.kilometers)
from surveys s
where exists (
    select *
    from specimens p
    where s.date=p.date
      and s.location=p.location
      and p.species = 'HASE'
)
select count(*), sum(s.kilometers)
from surveys s
where not exists (
    select *
    from specimens p
    where s.date=p.date
      and s.location=p.location
      and p.species = 'HASE'
)
样本
表中未找到
HASE
的调查:

select count(*), sum(s.kilometers)
from surveys s
where exists (
    select *
    from specimens p
    where s.date=p.date
      and s.location=p.location
      and p.species = 'HASE'
)
select count(*), sum(s.kilometers)
from surveys s
where not exists (
    select *
    from specimens p
    where s.date=p.date
      and s.location=p.location
      and p.species = 'HASE'
)
第一个查询的替代方案可以是:

select count(*), sum(s.kilometers)
from (
    select distinct date, location
    from specimens
    where species = 'HASE'
) p
join surveys s using (date, location)
根据数据(如果“HASE”是一种稀有的“物种”),它可能会更快


第二个查询的最佳选择可能已经由Barmar发布。

您可以在WHERE子句中使用
EXISTS
/
NOT EXISTS
子查询

样本
表中找到
HASE
的调查:

select count(*), sum(s.kilometers)
from surveys s
where exists (
    select *
    from specimens p
    where s.date=p.date
      and s.location=p.location
      and p.species = 'HASE'
)
select count(*), sum(s.kilometers)
from surveys s
where not exists (
    select *
    from specimens p
    where s.date=p.date
      and s.location=p.location
      and p.species = 'HASE'
)
样本
表中未找到
HASE
的调查:

select count(*), sum(s.kilometers)
from surveys s
where exists (
    select *
    from specimens p
    where s.date=p.date
      and s.location=p.location
      and p.species = 'HASE'
)
select count(*), sum(s.kilometers)
from surveys s
where not exists (
    select *
    from specimens p
    where s.date=p.date
      and s.location=p.location
      and p.species = 'HASE'
)
第一个查询的替代方案可以是:

select count(*), sum(s.kilometers)
from (
    select distinct date, location
    from specimens
    where species = 'HASE'
) p
join surveys s using (date, location)
根据数据(如果“HASE”是一种稀有的“物种”),它可能会更快


第二个查询的最佳选择可能已经由Barmar发布。

为什么人们发现连接如此困难

发现港口封条(HASE)的调查数量:

select count(distinct concat(s.location, s.date))
from surveys s 
Inner join specimens p 
on (s.date=p.date and s.location=p.location) 
where p.species = 'HASE';
找到未发现港口封条(HASE)的调查数量只是调查数量(您已经有)和上面的值之间的差异。由于两个查询都返回一行,因此查询的笛卡尔乘积将在单个输出行中给出一个值,但只是有点不同:

Select count(*), sum(kilometres)
From (
  Select kilometres
  From surveys s
  Left join specimens p 
  on (s.date=p.date and s.location=p.location) 
  and p.species = 'HASE'
  Where p.species is null
) As zero_surveys;

(上面还有其他几种编写查询的方法)

为什么人们发现连接如此困难

发现港口封条(HASE)的调查数量:

select count(distinct concat(s.location, s.date))
from surveys s 
Inner join specimens p 
on (s.date=p.date and s.location=p.location) 
where p.species = 'HASE';
找到未发现港口封条(HASE)的调查数量只是调查数量(您已经有)和上面的值之间的差异。由于两个查询都返回一行,因此查询的笛卡尔乘积将在单个输出行中给出一个值,但只是有点不同:

Select count(*), sum(kilometres)
From (
  Select kilometres
  From surveys s
  Left join specimens p 
  on (s.date=p.date and s.location=p.location) 
  and p.species = 'HASE'
  Where p.species is null
) As zero_surveys;

(有几种其他方法可以编写上述查询)

我想可能会丢失distinct。如果您在调查中使用distinct主键和样本,而不是日期+位置的组合,则会容易得多,因为您希望找到发现/未发现港口封条的调查数量。由于你的样本表也使用日期+位置,你必须决定它是否是一个特定的日期或时间,你会考虑一个“位置”。这导致了一个比必要的更复杂的问题/查询。这是什么:
from surveys=s left join examples=p
?不能将
=
放在表名和alias.Hmm之间。。不知何故,我找不到您的前两个查询之间的差异。我编辑了这篇文章以显示正确的第二个查询。我猜distinct可能会丢失。如果您对调查和样本使用不同的主键,而不是日期+位置的组合,则会容易得多,因为您希望找到需要的调查数量找到/未找到密封件。由于你的样本表也使用日期+位置,你必须决定它是否是一个特定的日期或时间,你会考虑一个“位置”。这导致了一个比必要的更复杂的问题/查询。这是什么:
from surveys=s left join examples=p
?不能将
=
放在表名和alias.Hmm之间。。不知何故,我找不到前两个查询之间的差异。我编辑了帖子以显示正确的第二个查询。这也是我对“未找到”查询的选择。这也是我对“未找到”查询的选择。“内部联接”没有帮助。问题在于,每个样本记录都与一个调查记录相匹配,并且许多调查都有多个样本,因此同一个调查会被多次计数。带有内部联接的查询得到的答案与带有左联接的查询相同:50904次调查,这是不可能的。@phper您的左联接是converte