多列条件对的快速SQL返回平均值

多列条件对的快速SQL返回平均值,sql,average,firebird,firebird-3.0,Sql,Average,Firebird,Firebird 3.0,假设有一张这样的桌子: --------------------------------------------------------------------- | id | val_1 | val_2 | ... | val_n | attr_1 | attr_2 | ... | attr_n | --------------------------------------------------------------------- val_i : DOUBLE PRECISION a

假设有一张这样的桌子:

---------------------------------------------------------------------
| id | val_1 | val_2 | ... | val_n | attr_1 | attr_2 | ... | attr_n |
---------------------------------------------------------------------

val_i  : DOUBLE PRECISION
attr_i : INTEGER
我需要得到每个平均值,其中attr_I等于某个值,每个attr都相同。如果某些属性i没有匹配项,则平均值i应为空,例如:

以下SQL似乎按预期工作:

SELECT
  AVG( t1."val_1" ),
  AVG( t2."val_2" ),
...
  AVG( tn."val_n" )
FROM "test" t1
FULL OUTER JOIN "test" t2 ON ( t2."attr_2" = t1."attr_1")
FULL OUTER JOIN "test" t3 ON ( t3."attr_3" = t1."attr_1")
...
FULL OUTER JOIN "test" tn ON ( tn."attr_n" = t1."attr_1")
WHERE
     ( t1."attr_1" = some_value )
  OR ( t2."attr_2" = some_value )
...
  OR ( tn."attr_n" = some_value )
;
但是太慢了。对于i==4和记录计数==100,我必须在约40分钟后中断操作


有没有更快的办法?最好是使用单个SQL而不是返回单行的存储过程。i==6和记录计数>1000最多需要几秒钟。

您的问题是相同的属性可以位于不同的列中,那么您的数据模型就有问题了。您可以通过取消激励和聚合来解决此问题:

select attr, avg(val)
from ((select t.id, t.attr_1 as attr, t.val_1 as val
       from t
      ) union all
      (select t.id, t.attr_2 as attr, t.val_n as val
       from t
      ) union all
      . . .
      (select t.id, t.attr_n as attr, t.val_n as val
       from t
      )
     ) t
group by attr;
在这种情况下,结果集是行而不是列


您可以在外部where或子查询中进行筛选。在子查询中进行过滤可能会有更好的性能-我对Firebird了解不够,无法知道过滤条件是否被下推到联合所有子查询中。

它没有像我预期的那样工作。它将所有val_i合并到一列中并返回其平均值,而我需要为每个val_i单独提供平均值。@oldscrawl将id添加到所有这些选择中above@Arioch'将结果按id拆分,但仍将所有val合并为一个column@OldSkull . . .平均数在不同的行上。答案解释了——或者至少它试图这么做。@GordonLinoff,id不是主键/autoinc。它可能会重复。例如,如果我有5个不同的id和4个val列,我将得到5个单独的行。如何提取我的4个平均值?是SELECT AVGIIFt1.attr_I=某个_值,t1.val_I,NULL,。。。从t1测试中,你在寻找什么?@BrakNicku是的,看起来是这样。谢谢,你得重新整理一下桌子。像这样的宽表不仅违反了数据库规范化规则,正如您刚刚了解到的那样,尝试这样做看起来很自然,而且它们的扩展性不好,特别是在IB/FB系列的多版本体系结构上。当您更新这样一个表并更改一列时,Firebird必须将整行写入磁盘。当您选择一列时,Firebird必须再次从磁盘读取整行。所以,更好的方法是将这个翻过来的表改成经典的id/val/attr方案,其中id/attr是主键,然后只需从中选择id、attr、avgval。。。按1,2-简单分组。如果您的一些遗留软件依赖于此数据透视布局,您可以创建一个模拟表的广泛设计的视图。但请注意,一旦您以后可能会添加更多的attr类型,您可能就没有列了@BrakNicku如果某些行中的某个值在attr_k而不是attr_i中得到满足,会怎么样?
select attr, avg(val)
from ((select t.id, t.attr_1 as attr, t.val_1 as val
       from t
      ) union all
      (select t.id, t.attr_2 as attr, t.val_n as val
       from t
      ) union all
      . . .
      (select t.id, t.attr_n as attr, t.val_n as val
       from t
      )
     ) t
group by attr;