Как мне «повернуть» или «сгладить» эти данные исследования?PIVOT, самостоятельное присоединение или что-то еще? - PullRequest
2 голосов
/ 16 ноября 2010

Мне очень трудно найти примеры, близкие к тому, что я делаю, или я просто не понимаю, какие примеры я нахожу.

У меня есть исследовательская база данных, которая содержит ответы людей на несколько вопросов в разные моменты времени.«Admin #» ниже представляет, какую «администрацию» теста представляют данные.Или вы можете думать о том, что в нем указано, какое «время» было дано для теста, например, время1, время2, время3

RespondentID# Admin# Question1 Question2 Question3 Question4 Question5
            1      1     A         B        C          D         E
            1      2     E         D        C          B         A
            1      3     Q         W        E          R         T
            2      1     Z         X        C          V         B
            2      2     P         O        I          U         Y
            2      3     Y         H        N          U         J

Теперь мне нужно упорядочить эти данные так, чтобы каждый набор ответовдля конкретного респондента находится в одном ряду.Таким образом, мы возьмем 5 полей вопросов и превратим их в 15 полей вопросов,

RespondentID# Admin1Question1 Admin1Question2 Admin1Question3 Admin1Question4 Admin1Question5 Admin2Question1 Admin2Question2 Admin2Question3 Admin2Question4 Admin2Question5 Admin3Question1 Admin3Question2 Admin3Question3 Admin3Question4 Admin3Question5 

. Как видите, каждое поле, начинающееся с Admin1, будет соответствовать строке в приведенном выше примере с администратором.# значение 1.

Пожалуйста, прости меня, если я не объясняю это должным образом.

Чтобы еще больше усложнить ситуацию, максимальное количество «администраций» или «раз» может увеличиться в будущем.В настоящее время это 3, но возможно, что в будущем один и тот же тест может проводиться 4, 5 или более раз.Какое бы решение не использовалось для этой проблемы, оно может быть статичным, а затем обновляться вручную для учета дополнительных «времен» в будущем, но было бы замечательно, если бы решение динамически учитывало неопределенное количество «времен».

Эти данные хранятся в базе данных MS SQL 2005, так что tsql, очевидно, является вариантом, но если существует лучшее решение в C # или LINQ (общий проект - приложение asp.net), я также открыт для этого.Все, что вы думаете, работает лучше всего!:)

Большое спасибо за чтение моего вопроса!

Ответы [ 3 ]

2 голосов
/ 16 ноября 2010

Я собираюсь взять каждую запись из 5 вопросов и нормализовать респондента, администратора и вопрос в список отдельных ответов, которые затем можно сгруппировать по респонденту.

var myResultsList = GetResultsFromDatabase();

var normalizedResults = myResultsList
   .SelectMany(r=>new[]{
      new{Respondent = r.RespondentId, Admin = r.AdminId, Question = 1, Answer= r.Question1},
      new{Respondent = r.RespondentId, Admin = r.AdminId, Question = 2, Answer = r.Question2},
      new{Respondent = r.RespondentId, Admin = r.AdminId, Question = 3, Answer = r.Question3},
      new{Respondent = r.RespondentId, Admin = r.AdminId, Question = 4, Answer = r.Question4},
      new{Respondent = r.RespondentId, Admin = r.AdminId, Question = 5, Answer = r.Question5},
   };

//finding a single answer, by respondent, admin and question:
normalizedList.FirstOrDefault(x=>x.Respondent == 1 && x.Admin == 2 && x.Question == 1);

Теперь у вас есть список анонимного типа с полями Респондент, Администратор, Вопрос и Ответ. Теперь вы можете сгруппировать эти элементы по респонденту и создать запрос (в основном, словарь списков) с ключом по респонденту:

var groupedResults = normalizedResults.GroupBy(r=>r.RespondentID);

//Get all records for Respondent # 1, ordered by Admin and Question:
var oneRespondentsResults = normalizedResults[1].OrderBy(x=>x.Admin).ThenBy(x=>x.Question);

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

var nestedDictionary = normalizedResults
    .ToDictionary(x=>x.Respondent,
        x=>nestedDictionary.Where(x2=>x2.Respondent == x.Respondent)
            .ToDictionary(x2=>x2.Admin,
                x2=>nestedDictionary.Where(x3=>x3.Respondent == x2.Respondent && x3.Admin == x2.Admin)
                    .ToDictionary(x3=>x3.Question, x3=>x3.Answer)));

//All that mess makes getting to a single value pretty easy:
var answer = nestedDictionary[1][2][1]; //Respondent 1, Admin 2, Question 1

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

2 голосов
/ 16 ноября 2010

После прочтения ответа @ KeithS я подумал о следующем подходе с использованием PIVOT и UNPIVOT:

Используйте UNPIVOT для нормализации исходных данных до RepsondentID, FullQuestionID, Answer:

select RespondentID,
       [FullQuestionID] = 'Admin'+cast(admin as varchar)+'_'+question,
       Answer
from (
       select RespondentID, Admin, Question1, Question2, Question3, Question4, Question5
       from tests
     ) t UNPIVOT (
       answer for question in (Question1, Question2, Question3, Question4, Question5)
     ) up

Затем используйте PIVOTчтобы отменить нормализацию данных в желаемом списке RespondentID:

;with data as (
  --unpivot code
)
select RespondentID, [Admin1_Question1], [Admin2_Question1], [Admin3_Question1]
from data
  PIVOT (min(Answer) for FullQuestionID in
     ([Admin1_Question1], [Admin2_Question1], [Admin3_Question1])
  ) p

Затем, наконец, вы можете использовать динамический t-sql для построения списка всех комбинаций Администратор / Вопрос.Все вместе выглядит следующим образом:

declare @list varchar(max)
select @list = coalesce(@list+',','')+'[Admin'+a+'_'+q+']'
from (select distinct cast(admin as varchar) a from tests) p1
    cross join (
        select 'Question1' q union
        select 'Question2' union
        select 'Question3' union
        select 'Question4' union
        select 'Question5'
    ) p2
order by a, q

declare @sql varchar(max)
set @sql =
';with data as (
    select RespondentID, [FullQuestionID]=''Admin''+cast(Admin as varchar)+''_''+question, Answer
    from (
        select respondentID, Admin, Question1, Question2, Question3, Question4, Question5
        from tests
        ) p
    UNPIVOT
        (answer for question in
            (Question1, Question2, Question3, Question4, Question5)
        ) as unPvt
)
select respondentID, '+@list+'
from data d
    PIVOT (min(answer) for FullQuestionID in
        ('+@list+')
    ) p'

exec(@sql)
2 голосов
/ 16 ноября 2010

Основной подход, который я придерживаюсь, заключается в ручном повороте с использованием кода, подобного следующему:

select RespondentID,
    min(case when Admin=1 then Question1 else null end) Admin1_Question1,
    min(case when Admin=2 then Question1 else null end) Admin2_Question1,
    min(case when Admin=3 then Question1 else null end) Admin3_Question1
from tests
group by RespondentID

Итак, используя динамический оператор t-sql, мы строим и выполняем запрос для этого следующим образом:

declare @select varchar(max)

select @select = coalesce(@select+',','')+
    'min(case when Admin='+a+' then '+q+' else null end) as [Admin'+a+'_'+q+']'
from (select distinct cast(Adminas varchar(10)) a from tests) p1
    cross join (
    select 'Question1' q union
    select 'Question2' union
    select 'Question3' union
    select 'Question4' union
    select 'Question5'
    ) p2
order by a, q


declare @sql varchar(max)
set @sql = 'select RespondentID, '+@select+' from tests group by RespondentID'

execute(@sql)

Это не самое динамичное решение t-sql, но оно должно работать!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...