在SQLite3中计算多列平均值

时间:2010-03-30 15:33:20

标签: sql sqlite survey average

我需要以 - 顺时针方式平均某些值,而不是 - 逐渐方式。 (如果我按列进行平均值,我可以使用avg())。我对此的具体应用要求我在平均中忽略NULL。这是非常简单的逻辑,但在SQL中看起来非常困难。有一种优雅的计算方法吗?

我正在使用SQLite3,因为它值得。

详情

如果您需要更多详细信息,请参阅以下内容:

我有一张调查表:

| q1 | q2    | q3    | ... | q144 |
|----|-------|-------|-----|------|
| 1  | 3     | 7     | ... | 2    |
| 4  | 2     | NULL  | ... | 1    |
| 5  | NULL  | 2     | ... | 3    |

(这些只是一些示例值和简单的列名。有效值为1到7和NULL。)

我需要像这样计算一些平均值:

q7 + q33 + q38 + q40 + ... + q119 / 11 as domain_score_1
q10 + q11 + q34 + q35 + ... + q140 / 13 as domain_score_2
...
q2 + q5 + q13 + q25 + ... + q122 / 12 as domain_score_14

...但我需要根据非空值拉出空值和平均值。所以,对于domain_score_1(有11个项目),我需要这样做:

Input:  3, 5, NULL, 7, 2, NULL, 3, 1, 5, NULL, 1

(3 + 5 + 7 + 2 + 3 + 1 + 5 + 1) / (11 - 3)
27 / 8
3.375

我正在考虑的一个简单算法是:

输入:

3, 5, NULL, 7, 2, NULL, 3, 1, 5, NULL, 1 

如果为NULL,则将每个值合并为0:

3, 5, 0, 7, 2, 0, 3, 1, 5, 0, 1

和:

27

通过转换值获取非零数量> 0到1和总和:

3, 5, 0, 7, 2, 0, 3, 1, 5, 0, 1
1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1
8

除以这两个数字

27 / 8
3.375

但这似乎比这应该采取更多的编程。是否有一种我不知道的优雅方式?

更新

除非我误解了某些内容,否则avg()将无效。我想做什么的例子:

select avg(q7, q33, q38, ..., q119) from survey;

输出:

SQL error near line 3: wrong number of arguments to function avg()

5 个答案:

答案 0 :(得分:4)

AVG已经忽略了null并做了你想要的事情:

  

avg()函数返回组内所有非NULL X的平均值。看起来不像数字的字符串和BLOB值被解释为0.只要存在至少一个非NULL输入,即使所有输入都是整数,avg()的结果也始终是浮点值。当且仅当没有非NULL输入时,avg()的结果为NULL。

来自http://www.sqlite.org/lang_aggfunc.html

因此,您可以获取每个域的值并将它们加载到另一个表中,然后在该表上运行平均值。或者你也可以忽略你的宽桌面并运行平均值。


AVG适用于列,而非行。因此,如果您在桌子上闲置,可以使用AVG而不会遇到您遇到的问题。我们来看一个小例子:

你有一张桌子,它看起来像这样:

ID  | q1  | q2  | q3
----------------------
1   | 1   | 2   | NULL
2   | NULL| 2   | 56

您希望将q1和q2放在一起,因为它们位于同一个域中,但它们是单独的列,因此您不能。但是如果你把你的桌子改成这样:

ID  | question | value
-----------------------
1   | 1        | 1
1   | 2        | 2
1   | 3        | NULL
2   | 1        | NULL
2   | 2        | 2
2   | 3        | 56

然后你可以轻松地取两个问题的平均值:

SELECT AVG(value)
FROM Table
WHERE question IN (1,2)

如果您想要每个ID的平均值而不是全局平均值,则可以按ID进行分组:

SELECT ID, AVG(value)
FROM Table
WHERE question IN (1,2)
GROUP BY ID

答案 1 :(得分:4)

在标准SQL中

SELECT 
(SUM(q7)+SUM(q33)+SUM(q38)+SUM(q40)+..+SUM(q119))/
(COUNT(q7)+COUNT(q33)+COUNT(q38)+COUNT(q40)+..+COUNT(q119)) AS domain_score1 
FROM survey

会给你你想要的东西如果为null,则SUM将合并到0,而COUNT将不计算NULL。 (希望SQLite3符合)。

编辑:检查http://www.sqlite.org/lang_aggfunc.html和SQLite符合;如果sum()要溢出,你可以使用total()代替。

另外我认为重新规范化的意见,如果你没有规范你的表设计(每当你看到名字中带有数字的列引起一个红色标志时)你就不会有优雅的SQL。

答案 2 :(得分:2)

这将是一个可怕的查询,但你可以这样做:

SELECT AVG(q) FROM
((SELECT q7 AS q FROM survey) UNION ALL
(SELECT q33 FROM survey) UNION ALL
(SELECT q38 FROM survey) UNION ALL
...
(SELECT q119 FROM survey))

这会将您的列转换为行并使用AVG()函数。

当然,您可能只想要一个特定的调查记录,所以不要忘记WHERE子句:

SELECT AVG(q) FROM
((SELECT q7 AS q FROM survey WHERE survey_id = 1) UNION ALL
(SELECT q33 FROM survey WHERE survey_id = 1) UNION ALL
(SELECT q38 FROM survey WHERE survey_id = 1) UNION ALL
...
(SELECT q119 FROM survey WHERE survey_id = 1))

如果将q列标准化为自己的表格,每行只有一个问题,并且引用回到调查,那么你会有更多的时间。调查与问题之间存在一对多的关系。

答案 3 :(得分:1)

使用单独的表格存储不同问题的调查分数(假设q是因为问题)。像下面这样的东西

SurveyTable(SurveyId, ...)
SurveyRatings(SurveyId, QuestionId, Rating)

之后您可以运行

之类的查询
SELECT avg(Rating) WHERE SurveyId=?

答案 4 :(得分:0)

使用:

SELECT AVG(x.answer)
  FROM (SELECT s.q7 AS answer
          FROM SURVEY s
        UNION ALL
        SELECT s.q33
          FROM SURVEY s
        UNION ALL    
       SELECT s.q38
         FROM SURVEY s
       ...
       UNION ALL
       SELECT s.q119
         FROM SURVEY s) x

不要使用UNION - 如果存在重复项,则需要重复。