列数= A的行的平均值,在另一列的不同行中,由第三列分组

时间:2011-06-04 14:01:12

标签: sql sql-server

使用SQL Server,我试图从我没有设计的表中查询一种平均计数,基本上我想要一个列表,按一列分组,另一列的不同值的数量匹配a给定标准,那些,与另一个标准匹配的行数(我将用它来创建平均计数或其他任何标准)。这可不难,但我的理论日设定不好,任何指针都会感激不尽。

这是简化和通用化的场景(下面的架构和示例数据)。假设我们有三列:

  • objid (具有聚集索引)
  • userid (没有索引,我可以添加一个)
  • actiontype (没有索引,我可以添加一个)

这些都不是唯一的,没有一个是null。我们希望完全忽略actiontypenone的所有行。根据{{​​1}},我们希望知道每个用户与之互动的对象平均有多少userid行。

因此,如果我们有“ahmed”,“joe”和“maria”,并且joe与3个对象进行了交互并提出了5个标志,那么actiontype = 'flag'个数字是连续的;如果“艾哈迈德”与3个物体互动并且没有举起任何旗帜,他的号码将是5 / 3 = 1.6666;如果“玛丽亚”与5个物体互动并举起4个旗帜,她的号码将是0

+--------+------------------+
| userid | flags_per_object |
+--------+------------------+
| ahmed  | 0                |
| joe    | 1.66666667       |
| maria  | 0.8              |
+--------+------------------+

如果将其作为副本关闭,我不会感到惊讶,我只是找不到它。

这是简化的表格设置和示例数据:

4 / 5 = 0.8

5 个答案:

答案 0 :(得分:5)

您可以尝试以下操作:

select  t1.userid,
CASE cnt2 
WHEN 0 THEN 0
ELSE ISNULL(cast(cnt2 as float)/cnt1,0)
END as num
FROM
(
  select userid, COUNT(distinct(t1.objid)) as cnt2
  from tmp as t1
  where t1.actiontype <> 'none'
  group by t1.userid
) t1

LEFT JOIN (
SELECT t2.userid, COUNT(*) as cnt1
FROM tmp as t2
WHERE t2.actiontype='flag'
GROUP BY t2.userid)b ON (b.userid = t1.userid)

即使它看起来比您的解决方案更糟糕,但令人惊讶的是,它会根据您提供的测试数据生成更好的执行计划。

答案 1 :(得分:2)

(Answering my own question.)

我确实有一些有用的东西:

select   userid,
         cast(count(case when actiontype = 'flag' then 1 else null end) as float)
         /
         count(distinct(objid))
         as flags_per_object
from     tmp
where    actiontype <> 'none'
group by userid

....但我不禁觉得有更好的方法......

答案 2 :(得分:1)

;with cte as
(
  select userid,
         sum(case actiontype when 'flag'
               then 1
               else 0
             end) as Flags
  from tmp
  where actiontype <> 'none'
  group by userid, [objid]
)
select userid,
       cast(sum(Flags) as float)/count(*) as flags_per_object
from cte
group by userid

作为子查询而不是CTE

select userid,
       cast(sum(Flags) as float)/count(*) as flags_per_object
from (select userid,
             sum(case actiontype when 'flag'
                   then 1
                   else 0
                 end) as Flags
      from tmp
      where actiontype <> 'none'
      group by userid, [objid]) as T
group by userid

答案 3 :(得分:1)

答案是:取决于。

在我的测试中,无论我使用什么测试数据,我的解决方案都是最慢的。使用真实数据,它的速度大约是最快解决方案的一半。

对于我的问题中引用的测试数据,

Mikael's solution更快,对于我现实生活中的表格中更大但仍然很小的数据集(我们的测试系统,大约2k行),速度更快。

a1ex07's solution对于我的全尺寸现实生活表(我们的实时系统,约700k行)来说更快。 a1ex07和Mikael之间的距离不是很远,但a1ex07肯定有优势。

我最终实际上使用的是Mikael的解决方案,因为如果你不是一个l33t的DB人员(并且对这段代码进行维护的人,其中SQL只是一小部分,那么它会更容易概念化) be)并且更容易适应各种其他场景。

因此,这个社区维基元回答,我会在时间限制过去时接受,而不是接受他们的优秀答案。如果您认为这有帮助,请像我一样对Mikael's answera1ex07's answer进行投票。

答案 4 :(得分:0)

SELECT
  userid,
  flags_per_object =
    COUNT(CASE actiontype WHEN 'flag' THEN objid END) *
    1.0 / COUNT(DISTINCT objid)
FROM
  tmp
WHERE actiontype <> 'none'
GROUP BY userid