Вычисление среднего по нескольким столбцам в SQLite3 - PullRequest
2 голосов
/ 30 марта 2010

Мне нужно усреднить некоторые значения в порядке строки , а не в столбце . (Если бы я делал среднее по столбцам, я мог бы просто использовать 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 

Объединяет каждое значение в 0, если NULL:

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 ]

4 голосов
/ 30 марта 2010

В стандартном 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

даст вам то, что вы хотите. SUM объединится в 0, если ноль и COUNT не будет считать NULL. (надеюсь, SQLite3 соответствует).

РЕДАКТИРОВАТЬ: проверил http://www.sqlite.org/lang_aggfunc.html и SQLite соответствия; если sum () будет переполнен, вы можете использовать total ().

Также я придерживаюсь мнения о нормализации, если вы не нормализуете дизайн таблицы (и всякий раз, когда вы видите, что столбцы с номерами в их именах поднимают красный флаг), у вас не будет элегантного SQL.

4 голосов
/ 30 марта 2010

AVG уже игнорирует нули и делает то, что вы хотите:

Функция avg () возвращает среднее значение всех ненулевых X в группе. Значения String и BLOB, которые не похожи на числа, интерпретируются как 0. Результат avg () всегда является значением с плавающей запятой, если имеется хотя бы один вход, отличный от NULL, даже если все входные данные являются целыми числами. Результат avg () равен NULL, если и только если нет входов, отличных от 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)

И вы можете группировать по идентификатору, если вам нужно среднее значение по идентификатору, а не глобальное среднее значение:

SELECT ID, AVG(value)
FROM Table
WHERE question IN (1,2)
GROUP BY ID
2 голосов
/ 30 марта 2010

Это будет чудовищный запрос, но вы можете сделать это:

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 в их собственной таблице с однимвопрос в строке и ссылки на опрос.Между опросом и вопросом у вас будет отношение 1 ко многим.

1 голос
/ 30 марта 2010

Используйте отдельную таблицу для хранения результатов опросов по разным вопросам (при условии, что q из-за вопроса). Что-то вроде следующего

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

После этого вы можете запустить запрос как

SELECT avg(Rating) WHERE SurveyId=?
0 голосов
/ 30 марта 2010

Использование:

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 - вам нужны дубликаты, если они существуют.

...