Использование SQL для преобразования представления модели EAV в реляционное представление может быть несколько запутанным и не очень эффективным.
Два наиболее часто используемых подхода - это условное агрегирование и коррелированные подзапросы в списке SELECT. Оба подхода требуют тщательного индексирования для подходящей производительности с большими наборами.
коррелированные подзапросы пример
Вот пример подхода коррелированных подзапросов, чтобы получить одинзначение атрибута "zipcode" для некоторых записей
SELECT r.id
, ( SELECT v1.value
FROM `apj32_facileforms_subrecords` v1
WHERE v1.record = r.id
AND v1.name = 'zipcode'
ORDER BY v1.value LIMIT 0,1
) AS `Zipcode`
FROM ( SELECT 1 AS id ) r
Расширяя это, мы повторяем коррелированный подзапрос, изменяя идентификатор атрибута ('firstname' вместо 'zipcode'. похоже, мы могли бы также ссылатьсяэто по элементам, например, v2.element = 2
SELECT r.id
, ( SELECT v1.value
FROM `apj32_facileforms_subrecords` v1
WHERE v1.record = r.id
AND v1.name = 'zipcode'
ORDER BY v1.value LIMIT 0,1
) AS `Zipcode`
, ( SELECT v2.value
FROM `apj32_facileforms_subrecords` v2
WHERE v2.record = r.id
AND v2.name = 'firstname'
ORDER BY v2.value LIMIT 0,1
) AS `First Name`
, ( SELECT v3.value
FROM `apj32_facileforms_subrecords` v3
WHERE v3.record = r.id
AND v3.name = 'lastname'
ORDER BY v3.value LIMIT 0,1
) AS `Last Name`
FROM ( SELECT 1 AS id UNION ALL SELECT 2 ) r
возвращает что-то вроде
id Zipcode First Name Last Name
-- ------- ---------- ---------
1 98228 David Bacon
2 98228 David Bacon
условное агрегирование пример приближения
Мы можемиспользуйте GROUP BY
, чтобы свернуть несколько строк в одну строку для каждой сущности, и используйте условные тесты в выражениях, чтобы "выбрать" значения атрибутов с помощью агрегатных функций.
SELECT r.id
, MIN(IF(v.name = 'zipcode' ,v.value,NULL)) AS `Zip Code`
, MIN(IF(v.name = 'firstname' ,v.value,NULL)) AS `First Name`
, MIN(IF(v.name = 'lastname' ,v.value,NULL)) AS `Last Name`
FROM ( SELECT 1 AS id UNION ALL SELECT 2 ) r
LEFT
JOIN `apj32_facileforms_subrecords` v
ON v.record = r.id
GROUP
BY r.id
Для более переносимого синтаксиса мы можем заменить MySQL *Функция 1029 * с более выражением стандарта ANSI CASE
, например,
, MIN(CASE v.name WHEN 'zipcode' THEN v.value END) AS `Zip Code`
Обратите внимание, что MySQL не поддерживает синтаксис SQL Server PIVOT
, или Oracle MODEL
синтаксис или Postgres CROSSTAB
или FILTER
синтаксис.
Чтобы расширить любой из этих подходов, чтобы он был динамическим, чтобы возвращать набор результатов с переменным числом столбцов и различными именами столбцов. .. это невозможно в контексте одного оператора SQL. Мы могли бы отдельно выполнять операторы SQL для извлечения информации, что позволило бы нам динамически создавать оператор SQL в форме, показанной выше, с явным набором возвращаемых столбцов.
Схема подходов, приведенная выше, возвращаетболее традиционная реляционная модель (отдельные столбцы со значениями).
нереляционные сопоставление атрибутов и значений в одну строку
Если у нас есть некоторые специальныев качестве разделителей мы могли бы объединить представление данных, используя функцию GROUP_CONCAT
В качестве элементарного примера:
SELECT r.id
, GROUP_CONCAT(v.title,'=',v.value ORDER BY v.name) AS vals
FROM ( SELECT 1 AS id ) r
LEFT
JOIN `apj32_facileforms_subrecords` v
ON v.record = r.id
AND v.name in ('zipcode','firstname','lastname')
GROUP
BY r.id
Чтобы вернуть два столбца, что-то вроде
id vals
-- ---------------------------------------------------
1 First Name=David,Last Name=Bacon,Zip Code=98228
Нам нужно знать, что возврат из GROUP_CONCAT ограничен group_concat_max_len
байтами. И здесь мы только что сжали шарик, перенеся задачу на более позднюю обработку, чтобы проанализировать полученную строку. Если у нас есть какие-либо знаки равенства или запятые, которые появляются в значениях, это приведет к беспорядку при разборе результирующей строки. Таким образом, нам придется корректно избегать любых разделителей, которые появляются в данных, так что выражение GROUP_CONCAT станет более сложным.