Как превратить вертикальный стол в горизонтальный стол? - PullRequest
1 голос
/ 15 апреля 2010

У меня есть один столик Персона:

Id Name
1  Person1
2  Person2
3  Person3

А у меня есть дочерняя таблица профиля:

Id PersonId FieldName  Value
1  1        Firstname  Alex
2  1        Lastname   Balmer
3  1        Email      some_email@test.com
4  1        Phone      +1 2 30004000

И я хочу получить данные из этих двух таблиц в одну строку следующим образом:

Id Name     Firstname Lastname  Email                Phone 
1  Person1  Alex      Balmer    some_email@test.com  +1 2 30004000
  1. Каков наиболее оптимизированный запрос для получения этих вертикальных значений (ключ, значение) в одной строке, как эта? Теперь у меня проблема в том, что я сделал четыре соединения дочерней таблицы с родительской таблицей, потому что мне нужно получить эти четыре поля. Некоторая оптимизация наверняка возможна.
  2. Я бы хотел иметь возможность легко изменять этот запрос, когда добавляю новое поле (ключ, значение). Каков наилучший способ сделать это? Чтобы создать хранимую процедуру?

Я хотел бы, чтобы в моём слое БД (C #) и при использовании LINQ (при программировании) были строго введены типы, поэтому это означает, что когда я добавляю пару новых ключей, значений в таблицу профилей, я хотел бы внести минимальные изменения в БД и C # если возможно. На самом деле я пытаюсь получить некоторые лучшие практики в этом случае.

Ответы [ 2 ]

1 голос
/ 15 апреля 2010

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

CREATE TABLE Persons
(PersonID     int identity(1,1) primary key
,Firstname    varchar(...)
,Lastname     varchar(...)
,Email        varchar(...)
,Phone        varchar(...)
,....
)

тогда наиболее оптимизированный запрос будет:

SELECT
    PersonID,Firstname,Lastname,Email,Phone
    FROM Persons
    WHERE ...

Добавьте все основные столбцы в таблицу персон. если вам нужно специализироваться, создайте дополнительные таблицы:

--one person can play many instruments with this table
CREATE TABLE PersonMusicians
(PersonID               int           --pk   fk to Persons.PersonID     
,InstrumentCode         char(1)       --pk
,...
)

--only one row per person with this table
CREATE TABLE PersonTeachers
(PersonID               int            --pk   fk to Persons.PersonID    
,FavoriteSubjectCode    char(1)
,SchoolName             varchar(...)
)

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

AdditionalInfoFields
FieldID    int identity(1,1) primary key
FieldName  varchar(...)

AdditionalInfo
AdditionalInfoID   int identity(1,1) primary key
PersonID           int   fk to Persons.PersonID  
FieldID            int   fk to AdditionalInfoFields.FieldID    
FieldValue         varchar(..) or you can look into sql_variant
У

есть индекс на AdditionalInfo.PersonID+FieldID, и если вы будете искать всех людей, имеющих атрибут X, тогда есть и другой, такой как AdditionalInfo.FieldID+PersonID

Если не считать ничего из вышеперечисленного, вам нужно будет использовать четыре левых внешних соединения, как вы упомянули в своем варианте №1:

SELECT
    P.ID, p.Name
        , p1.Value AS Firstname
        , p2.value AS Lastname     
        , p3.Value AS Email
        , p4.Value AS Phone
    FROM Persons                 p
        LEFT OUTER JOIN Profile p1 ON p.PersonID=p1.PersonID AND p1.FieldName='Firstname'
        LEFT OUTER JOIN Profile p1 ON p.PersonID=p1.PersonID AND p1.FieldName='Lastname'
        LEFT OUTER JOIN Profile p1 ON p.PersonID=p1.PersonID AND p1.FieldName='Email'
        LEFT OUTER JOIN Profile p1 ON p.PersonID=p1.PersonID AND p1.FieldName='Phone'
    WHERE ....

Вы всегда можете создать материализованное представление с индексом из этого 4-го левого запроса соединения и иметь для вас данные, рассчитанные заранее, что должно ускорить его.

1 голос
/ 15 апреля 2010
Select 
    P.ID
    , P.Name
    , Case When C.FieldName = 'FirstName' Then C.Value Else NULL END AS FirstName
    , Case When C.FieldName = 'LastName' Then C.Value Else NULL END AS LastName
    , Case When C.FieldName = 'Email' Then C.Value Else NULL END AS Email
    , Case When C.FieldName = 'Phone' Then C.Value Else NULL END AS Phone  
From Person AS P
Inner JOIN Child AS C
ON P.ID = C.PersonID

Вы можете использовать PIVOT; не уверен, какой из них будет проще всего добавить новый столбец.

...