我需要以行 - 顺时针方式平均某些值,而不是列 - 逐渐方式。 (如果我按列进行平均值,我可以使用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()
答案 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
- 如果存在重复项,则需要重复。