1

我试图弄清楚如何使用多个左外连接来计算平均分数和卡片数量。我有以下架构和测试数据。每副牌有 0 或更多的分数和 0 或更多的牌。我需要计算每副牌的平均分数和卡数。为了方便起见,我使用 mysql,我最终希望它在 Android 手机上的 sqlite 上运行。

mysql> 从卡组中选择 *;
+----+-------+
| 编号 | 姓名 |
+----+-------+
| 1 | 一个 |
| 2 | 二 |
| 3 | 三 |
+----+-------+
mysql> 从分数中选择 *;
+---------+-------+----------+--------+
| 分数 ID | 价值 | 日期 | 甲板编号 |
+---------+-------+----------+--------+
| 1 | 6.58 | 2009-10-05 20:54:52 | 1 |
| 2 | 7 | 2009-10-05 20:54:58 | 1 |
| 3 | 4.67 | 2009-10-05 20:55:04 | 1 |
| 4 | 7 | 2009-10-05 20:57:38 | 2 |
| 5 | 7 | 2009-10-05 20:57:41 | 2 |
+---------+-------+----------+--------+
mysql> 从卡中选择 *;
+--------+--------+------+--------+
| 卡号 | 正面 | 返回 | 甲板编号 |
+--------+--------+------+--------+
| 1 | 前 | 返回 | 2 |
| 2 | 前 | 返回 | 1 |
| 3 | f1 | b2 | 1 |
+--------+--------+------+--------+

我运行以下查询...

mysql> 选择deck.name, sum(score.value)/count(score.value) "Ave",
    -> 计数(card.front)“计数”
    -> 从甲板
    -> 在deck.id=score.deckId 上的左外连接分数
    -> 在deck.id=card.deckId 上的左外连接卡
    ->按deck.id分组;

+--------+------+--------+
| 姓名 | 大道 | 计数 |
+--------+------+--------+
| 一个 | 6.0833333333333 | 6 |
| 二 | 7 | 2 |
| 三 | 空 | 0 |
+--------+------+--------+

......我得到了平均值的正确答案,但卡片数量的错误答案。在我拔头发之前,有人能告诉我我做错了什么吗?

谢谢!

约翰

4

4 回答 4

1

它正在运行您所要求的 - 它将卡片 2 和 3 加入得分 1、2 和 3 - 创建 6 (2 * 3) 的计数。在卡片 1 的情况下,它连接到分数 4 和 5,产生 2 (1 * 2) 的计数。

如果您只想计算卡片数量,就像您目前正在做的那样,请使用 COUNT(Distinct Card.CardId)。

于 2009-10-10T01:48:32.147 回答
1
select deck.name, coalesce(x.ave,0) as ave, count(card.*) as count -- card.* makes the intent more clear, i.e. to counting card itself, not the field.  but do not do count(*), will make the result wrong
from deck    
left join -- flatten the average result rows first
(
    select deckId,sum(value)/count(*) as ave -- count the number of rows, not count the column name value.  intent is more clear
    from score 
    group by deckId
) as x on x.deckId = deck.id
left outer join card on card.deckId = deck.id -- then join the flattened results to cards
group by deck.id, x.ave, deck.name
order by deck.id

[编辑]

sql 有内置的平均函数,只需使用这个:

select deckId, avg(value) as ave
from score 
group by deckId
于 2009-10-10T02:32:57.500 回答
1

出了问题的是您正在创建和之间的笛卡尔积scorecard

它是这样工作的:当你加入deckscore,你可能会有多行匹配。然后将这些多行中的每一连接到. 没有条件阻止这种情况发生,并且没有条件限制时的默认连接行为是将一个表中的所有行连接到另一个表中的所有行。card

要查看它的实际效果,请尝试此查询,不使用 group by:

select * 
from deck 
left outer join score on deck.id=score.deckId 
left outer join card on deck.id=card.deckId;

您会在来自score和的列中看到大量重复数据card。当您计算其中AVG()重复的过度数据时,冗余值会神奇地消失(只要这些值均匀重复)。但是当你COUNT()SUM()他们时,总数是很远的。

对于无意的笛卡尔积可能有补救措施。在您的情况下,您可以使用COUNT(DISTINCT)来补偿:

select deck.name, avg(score.value) "Ave", count(DISTINCT card.front) "Count" 
from deck 
left outer join score on deck.id=score.deckId 
left outer join card on deck.id=card.deckId
group by deck.id;

此解决方案不能解决所有无意的笛卡尔积的情况。更通用的解决方案是将其分解为两个单独的查询:

select deck.name, avg(score.value) "Ave"
from deck 
left outer join score on deck.id=score.deckId 
group by deck.id;

select deck.name, count(card.front) "Count" 
from deck 
left outer join card on deck.id=card.deckId
group by deck.id;

并非数据库编程中的每项任务都必须在单个查询中完成。当您需要多个统计信息时,使用单个查询甚至可以有效(以及更简单、更容易修改和更不容易出错)。

于 2009-10-10T05:22:54.017 回答
0

在我看来,使用左连接不是一个好方法。这是您想要的结果的标准 SQL 查询。

select
  name,
  (select avg(value) from score where score.deckId = deck.id) as Ave,
  (select count(*) from card where card.deckId = deck.id) as "Count"
from deck;
于 2009-10-10T03:41:33.533 回答