SQL - столбцы для разных категорий - PullRequest
12 голосов
/ 31 августа 2010

Я новичок в SQL.У меня есть база данных с данными для разных экзаменов, например:

Student Test Grade
--------------------
St1    T1   A
St2    T1   B
St3    T1   B
St1    T2   B
St2    T2   B
St3    T2   A
St1    T3   A
St2    T3   C
St3    T3   B

Затем я хотел бы напечатать отчет, используя тесты (T1, T2 и T3) в виде столбцов:

Student  T1   T2   T3
----------------------
St1      A    B    A
St2      B    B    C
St3      B    A    B

Я пробовал разные вещи, но я застрял на том, как сделать такую ​​распечатку.Любая помощь приветствуется!

Ответы [ 6 ]

11 голосов
/ 31 августа 2010

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

  SELECT t.student,
         MAX(CASE WHEN t.test = 'T1' THEN t.grade END) AS T1,
         MAX(CASE WHEN t.test = 'T2' THEN t.grade END) AS T2,
         MAX(CASE WHEN t.test = 'T3' THEN t.grade END) AS T3
    FROM TABLE t
GROUP BY t.student
1 голос
/ 31 августа 2010

Есть несколько способов сделать это, оба из которых (в чистом SQL, а не в коде, генерирующем команду SQL) требуют, чтобы число столбцов было известно и исправлено.Наиболее простым для реализации было бы:

SELECT eg.Student,
(SELECT Grade from ExamGrade eg1 WHERE eg1.Student = eg.Student AND Test = 'T1') AS T1
(SELECT Grade from ExamGrade eg2 WHERE eg2.Student = eg.Student AND Test = 'T2') AS T2
(SELECT Grade from ExamGrade eg3 WHERE eg3.Student = eg.Student AND Test = 'T3') AS T3
FROM ExamGrade eg

Это будет работать практически в любой среде, включая SQLite, и его можно было бы сделать немного более элегантным с помощью скалярной функции GetTest (), которая будет принимать ученикаи номер теста и вернуть оценку.Однако, в любом случае, это не является ни производительным, ни закрытым для изменения;он запросит N-квадрат таблицы для N тестов, и если вы добавите 4-й тест, этот запрос придется изменить, чтобы включить его в отчет.

Если комбинация Student и Test уникальна,и вы работаете в базе данных с функциональностью Pivot (которой, по-видимому, нет в SQLite), вы можете использовать запрос Pivot практически с любым агрегатором (MAX / MIN / AVG / SUM для набора с одним значением таков:значение).Следующие работы в MSS2005:

SELECT Student, T1, T2, T3
FROM (Select Student, Test, Grade FROM ExamGrade) As SourceQuery
PIVOT (MAX(Grade) FOR Test IN (T1, T2, T3)) AS PivotTable

Это будет более производительным и гораздо более элегантным.Списки столбцов по-прежнему не могут быть определены динамически AFAIK, но их тривиально сгенерировать, если вы делаете этот запрос из кода приложения или используете встроенный хранимый процесс sp_executesql в MS SQL Server для генерации запроса из другого сохраненного процесса илифункция.

1 голос
/ 31 августа 2010

Я полагаю, что если вы собираетесь расширить эту систему, включив в нее больше информации, вы могли бы извлечь пользу от доработки своей базы данных, я бы построил ее так:

Название таблицы = Полужирный

Имя столбца = Курсив

Студенты

  • SID (первичный ключ)
  • Другая информация о студенте

Тесты :

  • TID (первичный ключ)
  • Другая информация о тесте

Тестовые оценки

  • GID (первичный ключ)
  • TID (внешний ключ)
  • SID (внешний ключ)
  • Оценка

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

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

например. У ученика 1 - SID 1, а во всех его тестах - 1 в столбце SID. то же самое для ученика 2, 3, 4 и т. д.

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

Чтобы получить то, что вы хотите из этих таблиц, я бы использовал это (написано на PHP):

$sql = 'SELECT * FROM Tests ORDER BY TID';
$tempresult = mysql_query($sql);
while($temprow = mysql_fetch_array($tempresult)){
    echo $temprow['TID'];
}

$sql = 'SELECT * FROM Students';
$result = mysql_query($sql);
while($row = mysql_fetch_array($result)){
   echo '\n'.$row['SID'];
   $sql = 'SELECT * FROM Grades WHERE SID='.$row['SID'].' ORDER BY TID';
   $result2 = mysql_query($sql);
   while($row2 = mysql_fetch_array($result2)){
      echo ' '.$rows['Grade'];
   }
}

Вы можете добавить форматирование к этому в выражениях echo, а также распечатать любую дополнительную информацию, которую вы решите добавить. Если у вас есть какие-либо вопросы, пожалуйста, задавайте их.

РЕДАКТИРОВАТЬ: Я прочитал другие, и согласен, что их метод, по всей вероятности, лучше, единственное, в чем я не уверен, так это то, могут ли сводные таблицы расширяться для обработки различного количества тестов Если это возможно (или вам не нужно), я предлагаю их метод, иначе я чувствую, что это может найти место в вашем приложении.

1 голос
/ 31 августа 2010

Я задал похожий вопрос некоторое время назад.Вам нужно что-то похожее на сводную таблицу, но это не доступно в SQLite (насколько я знаю).

0 голосов
/ 03 мая 2018

Я хотел бы добавить некоторые пояснения к ответу @OMG_Ponies, потому что это может быть полезно для пользователей, не являющихся суперпользователями SQL (таких, как я)

Давайте создадим пример таблицы и добавим фиктивные данные:

CREATE TABLE t (
   t_ID integer primary key autoincrement not null,
   student integer,
   test text,
   grade text
);

INSERT INTO t 
   (student, test, grade)
VALUES
   ('St1', 'T1', 'A'),
   ('St2', 'T1', 'B'),
   ('St3', 'T1', 'B'),
   ('St1', 'T2', 'B'),
   ('St2', 'T2', 'B'),
   ('St3', 'T2', 'A'),
   ('St1', 'T3', 'A'),
   ('St2', 'T3', 'C'),
   ('St3', 'T3', 'B');

Итак, у нас есть следующее:

t_ID student test grade
-------------------------
 1   St1     T1     A
 2   St2     T1     B
 3   St3     T1     B
 4   St1     T2     B
 5   St2     T2     B
 6   St3     T2     A
 7   St1     T3     A
 8   St2     T3     C
 9   St3     T3     B

Используя выражение case when ... then ... end, можно получить нужные столбцы

SELECT 
   t_ID,
   student,
   (case when t.test = 'T1' then t.grade end) as T1,
   (case when t.test = 'T2' then t.grade end) as T2,
   (case when t.test = 'T3' then t.grade end) as T3
FROM t
   order by student

Результат

t_ID student  T1    T2     T3
----------------------------------
1    St1      A    NULL  NULL       
4    St1     NULL   B    NULL   
7    St1     NULL  NULL   A
2    St2      B    NULL  NULL   
5    St2     NULL   B    NULL   
8    St2     NULL  NULL   C
3    St3      B    NULL  NULL   
6    St3     NULL   A    NULL   
9    St3     NULL  NULL   B 

Однако мы видим, что необходимо сгруппировать результаты по полю «студент».Когда мы группируем, мы должны указать агрегатную функцию, чтобы указать, какое значение сохранить в случае наличия более одной строки с одним и тем же значением «student».В этом случае мы используем функцию «max», чтобы отбросить нулевые значения.

SELECT 
   t_ID,
   student,
   max(case when t.test = 'T1' then t.grade end) as T1,
   max(case when t.test = 'T2' then t.grade end) as T2,
   max(case when t.test = 'T3' then t.grade end) as T3
FROM t
GROUP BY student
ORDER BY student

Результат

t_ID student  T1   T2   T3
-----------------------------
7    St1      A    B     A      
8    St2      B    B     C  
9    St3      B    A     B

Заключительная нота.Так как мы не сгруппировали также по t_ID и не указали для него агрегатную функцию, вы должны предположить, что значение t_ID каждой строки является случайным для каждой группы.Будьте осторожны с этим.

0 голосов
/ 31 августа 2010

Попробуйте

SELECT Student, MAX(CASE WHEN Test = 'T1' THEN Grade END) AS T1,
   MAX(CASE WHEN Test = 'T2' THEN Grade END) AS T2,
   MAX(CASE WHEN Test = 'T3' THEN Grade END) AS T3 FROM tablename  GROUP BY Student

Используйте имя таблицы вместо "tablename".

...