Есть ли способ выбрать нормализованные данные обратно в один набор результатов

#mysql #sql #database

#mysql #sql #База данных

Вопрос:

У меня есть три таблицы. Foo, Attrib, FooAttrib . Где FooAttrib — это мост между foo и attrib . Как вы можете видеть, в таблице много foo и много атрибутов.

  Foo                 FooAttri                          Attrib
 ------------      -----------------------------     --------------
| id |  Name |    | fooId   |  attribId | value |   | id | Descrip |
|----|-------|    |---------|-----------|-------|   |----|---------|  
|  1 |  Sam  |    |  1      |     1     | red   |   |  1 | Color   |
|  2 |  Bill |    |  1      |     2     | Grape |   |  2 | Flavor  |
|  3 |  Ted  |    |  2      |     1     | Blue  |   |  3 | Weight  |
 ------------     |  3      |     3     |  10   |    --------------
                  |  1      |     3     |   5   |
                  |  2      |     3     |   1   |
                   -----------------------------
  

Я знаю, как получить следующее:

 fooId   Name     Attrib     Value
1       Sam      Color      red     
1       Sam      Flavor     Grape    
1       Sam      Weight     5
2       Bill     Color      Blue
2       Bill     Weight     1
3       Ted      Weight     10
  

Но что я хочу знать; Возможно ли создать оператор select, чтобы я получал данные foo и attrib, возвращаемые в одном результирующем наборе, который выглядит следующим образом?

 fooId   Name     Color   Flavor   Weight
1       Sam      red     Grape    5
2       Bill     Blue             1
3       Ted                       10
  

Комментарии:

1. У вас есть базовая модель EAV (Entity-Attribute-Value), которая представляет собой моделирование, ориентированное на столбцы, а процесс преобразования из модели столбца> строки называется поворотом. Поиск в Google для поворота или поиска на этом сайте того же термина должен дать вам представление о том, как это сделать, однако лучший подход — сделать это в приложении (php, c, python или что бы вы ни использовали), чем пытаться сделать это в MySQL.

2. @N.B. Спасибо за ответ, сама терминология очень полезна. Я попробую.

3. Вы понимаете, что модель данных EAV очень сложна для запроса и очень плоха для производительности. Это наихудший возможный дизайн для большинства вещей в реляционной базе данных и является антипаттером SQL. Если вам действительно нужен EAV, я бы предложил базу данных nosql.

Ответ №1:

Вы могли бы сделать это, если знаете общее количество возможных аргументов, которые вы хотите в момент выбора:

 select f.id, f.name, fa_color.value as color, , fa_flavor.value as flavor
from foo f
join fooattri fa_color on f.id = fa_color.fooid and fa_color.attribid = 1
join fooattri fa_flavor on f.id = fa_flavor.fooid and fa_flavor.attribid = 2
...
  

Комментарии:

1. Спасибо за ответ… что, если я не знаю количество атрибутов, которые у меня будут? Или другой способ задать этот вопрос: что, если список атрибутов может измениться (к нему добавлено больше)?

2. Проблема при повороте заключается в том, что вы должны знать максимум. количество атрибутов, чтобы вы знали, сколько раз вам нужно самостоятельно присоединяться к таблице. В противном случае вы не сможете получить обычный макет. Вот почему люди обычно делают этот поворот на языках приложений, поскольку это проще и быстрее. Если вы собираетесь запросить свой набор данных EAV, создав сводную таблицу или представление — не беспокойтесь, это будет ужасно, ужасно медленно.

3. С таблицами EAV вы должны заранее знать количество атрибутов, потому что вам нужно присоединиться к таблице один раз для каждого атрибута. Это одна из причин, по которой этот дизайн является очень плохой идеей.

4. Да, я предоставляю этот ответ как возможное «решение». Но это может работать только в ограниченном подмножестве ситуаций.

5. @kasdega Другим возможным «дополнением» к этому «решению» было бы сначала запросить возможные атрибуты, а затем создать на их основе динамический оператор select. Но переход «по вертикали» намного лучше.

Ответ №2:

Попробуйте это, этот метод обычно работает для меня:

 SELECT fo.id, fo.name, 
  (SELECT fooattri.value 
  FROM fooattri 
    INNER JOIN attrib ON attrib.id = fooattri.attribid 
  WHERE attrib.descrip = 'Color' 
    AND fooattri.fooid = fo.id) AS Color, 
  (SELECT fooattri.value 
  FROM fooattri 
    INNER JOIN attrib ON attrib.id = fooattri.attribid 
  WHERE attrib.descrip = 'Flavor' 
    AND fooattri.fooid = fo.id) AS Flavor, 
  (SELECT fooattri.value 
  FROM fooattri 
    INNER JOIN attrib ON attrib.id = fooattri.attribid 
  WHERE attrib.descrip = 'Weight' 
    AND fooattri.fooid = fo.id) AS Weight 
FROM foo fo