Как я могу «транспонировать» свои данные с помощью SQL и одновременно удалять дубликаты? - PullRequest
0 голосов
/ 28 мая 2010

В моей базе данных есть следующая структура данных:

LastName    FirstName    CourseName
John        Day          Pricing
John        Day          Marketing
John        Day          Finance
Lisa        Smith        Marketing
Lisa        Smith        Finance
etc...

Данные показывают занятость в бизнесе и какие курсы они предпочитают посещать. Количество курсов на одного работника будет различным (т. Е. Как указано выше, у Джона есть 3 курса, а у Лизы 2).

Мне нужно взять эти данные из базы данных и передать их в представление веб-страницы (asp.net mvc).

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

LastName    FirstName    Course1    Course2    Course3
John        Day          Pricing    Marketing  Finance
Lisa        Smith        Marketing  Finance

Есть мысли о том, как этого можно достичь?


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

<% foreach (var item in Model.courseData) { %>

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

Я пытался преобразовать данные с помощью c # в моем ViewModel, но мне было трудно, и я чувствую, что могу облегчить рабочую нагрузку, используя SQL, прежде чем вернуть данные.

Спасибо.

Ответы [ 6 ]

1 голос
/ 28 мая 2010

Это можно сделать прямым SQL ( пример выполнения ):

DECLARE @data AS TABLE (LastName varchar(25), FirstName varchar(25), CourseName varchar(25))

INSERT INTO @data VALUES ('John', 'Day', 'Pricing')
INSERT INTO @data VALUES ('John', 'Day', 'Marketing')
INSERT INTO @data VALUES ('John', 'Day', 'Finance')
INSERT INTO @data VALUES ('Lisa', 'Smith', 'Marketing')
INSERT INTO @data VALUES ('Lisa', 'Smith', 'Finance')

SELECT *
FROM (
SELECT LastName, FirstName, CourseName, Bucket = 'Course' + CAST(ROW_NUMBER() OVER(PARTITION BY LastName, FirstName ORDER BY CourseName) AS varchar) FROM @data
) AS n PIVOT (MIN(CourseName) FOR Bucket IN ([Course1], [Course2], [Course3])) AS pvt
1 голос
/ 28 мая 2010
ПРИМЕЧАНИЕ. Я не хочу использовать функцию Database PIVOT, поскольку число классов, скорее всего, изменится, и вам придется обновить SQL-запрос.

То, что вы пытаетесь сделать, не должно быть сделано в представлении.
Объект Model должен быть достаточно простым для отображения при передаче в представление.
Потому что в View не должно быть сложной логики.

Следовательно, нам нужно предварительно обработать данные, возвращенные из базы данных SQL Server в первую очередь.


Я предполагаю, что у вас есть такой объект, потому что вы не упомянули, что у него есть уникальный идентификатор, такой как «CustomerId» или что-то в этом роде.

public class CourseData {
    public string FirstName { get; set; }
    public string LastName  { get; set; }
    public string ClassName { get; set; }
}

Это будет мой объект [Model].

public class MyViewModel {
    public IDictionary<string, IList<string>> CourseData { get; set; }
    public int MaxCourseCount { get; set; }
}

Это мой метод действия в контроллере.

public ActionResult MyView()
{
    IDictionary<string, IList<string>> dict = new Dictionary<string, IList<string>>();

    // retrieve data from the database
    IList<CourseData> result = RetrieveData();
    foreach (var item in result)
    {
        // [FirstName] and [LastName] combo will be used as KEY entry
        string key = item.FirstName + " " + item.LastName;

        if (dict.ContainsKey(key))
        {
            // add the class name into an existing "string" collection
            dict[key].Add( item.ClassName );
        }
        else
        {
            // instantiate a new "string" collection and add the class name.
            dict[key] = new List<string> { item.ClassName };
        }
    }

    // find out which Person has attended the most number of classes.
    int maxCourseCount = 0;
    foreach (var key in dict.Keys)
    {
        int valueCount = dict[key].Count;
        if (valueCount > maxCourseCount)
            maxCourseCount = valueCount;
    }


    MyViewModel model = new MyViewModel {
        CourseData = dict,
        MaxCourseCount = maxCourseCount
    };
    return View(model);
}

Я объединил данные в структуру данных, которую легче рендерить. Я намеренно добавил свойство [MaxCourseCount] к моему объекту Model, поскольку кажется, что вы хотите отобразить данные курса в формате .

Итак, все, что вам нужно сделать сейчас, это

  1. перебирает ключи вашего словаря Model.CourseData объекта.
  2. отображает названия курсов в отдельных ячейках таблицы.
  3. рендеринг оставшихся ячеек таблицы на основе значения [MaxCourseCount] вашей модели.

Надеюсь, это поможет.

-Soe

1 голос
/ 28 мая 2010

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

Я бы нормализовал ваши данные в три таблицы. Один содержит людей с именем Person (с некоторым первичным ключом - ваш звонок), а другой - с курсами под названием Course (опять же, с некоторой формой первичного ключа). Затем вы можете обработать свои сопоставления в третьей таблице (назовите ее Schedule), которая связывает первичный ключ Person с первичным ключом Course.

Если я смогу добраться до места, где смогу вывести диаграмму ER, я опубликую сообщение о редактировании.

Как только ваши данные упорядочены, это значительно облегчает задачу их отображения и упорядочивания.

1 голос
/ 28 мая 2010

Вы можете транспонировать данные, используя PIVOT, но вы получите курсы в виде имен столбцов, а 1 или 0 - данные для посещения / не посещения. Используя это и немного кода в ViewModel, вы получите то, что вы хотите:

SELECT  * FROM 
(SELECT FirstName, LastName, Course FROM Courses) src
PIVOT (COUNT(Course) FOR Course IN ([Finance] ,[Marketing],[Pricing])) AS pvt

Что дает вам:

FirstName    LastName  Finance Marketing Pricing
--------------------------------------------------------------
John         Day       1       1         1
Lisa         Smith     1       1         0
0 голосов
/ 28 мая 2010

В данных образца нет ничего, что указывало бы на приоритет. Например, как мы узнаем, что первым выбором Дня Джона является Ценообразование? Предполагая, что у вас есть такой столбец, вы можете сделать что-то вроде:

Select LastName, FirstName
    , Min( Case When Priority = 1 Then CourseName End ) As Course1
    , Min( Case When Priority = 2 Then CourseName End ) As Course2  
    , Min( Case When Priority = 3 Then CourseName End ) As Course3
From Table
Group By LastName, FirstName
0 голосов
/ 28 мая 2010

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

LastName    FirstName    Courses
John        Day          Pricing, Marketing, Finance
Lisa        Smith        Marketing, Finance
...