Лучше SQL-запрос? - PullRequest
       6

Лучше SQL-запрос?

3 голосов
/ 16 июня 2009

Структура БД:

fid  
subid  
fieldname  
fieldval 

Чтобы получить запись для человека, я делаю что-то вроде этого:

$querystr = "
SELECT FN.sub_id, FN.`First Name` , LN.`Last Name` , DOB.`dob` , EMAIL.`email` , PHONE.`phone`  
FROM  
( SELECT sub_id, field_val AS 'First Name'
FROM $db->data
WHERE `field_name` = 'First Name'
)FN,   
(  SELECT sub_id, field_val AS 'Last Name'
FROM $db->data
WHERE `field_name` = 'Last Name'
)LN,  
( SELECT sub_id, field_val AS `Team`
FROM $db->data
WHERE `field_name` = 'Team'
)TEAM,  
( SELECT sub_id, field_val AS `dob`
FROM $db->data
WHERE `field_name` = 'DOB'
)DOB,  
( SELECT sub_id, field_val AS `email`
FROM $db->data
WHERE `field_name` = 'EMail'
)EMAIL,  
( SELECT sub_id, field_val AS `phone`
FROM $db->data
WHERE `field_name` = 'Telephone'
)PHONE  

WHERE FN.sub_id = LN.sub_id  
AND LN.sub_id = DOB.sub_id  
and DOB.sub_id = EMAIL.sub_id  
and EMAIL.sub_id = PHONE.sub_id  
ORDER BY LN.`Last Name`  
 ";

Есть предложения, как это упростить?

Ответы [ 7 ]

6 голосов
/ 16 июня 2009

Вы можете сделать эти многочисленные самостоятельные объединения таблицы data более явными, что сделает запрос более читабельным, но, скорее всего, не повлияет на скорость. I.e.:

SELECT FN.sub_id, FN.field_val AS `First Name`, 
                  LN.field_val AS `Last Name`, 
                  DOB.field_val AS `dob`, 
                  EMAIL.field_val AS `email`, 
                  PHONE.field_val AS `phone`  
FROM  $db->data FN
JOIN  $db->data LN ON (LN.field_name = 'Last Name' AND LN.sub_id = FN.sub_id)
JOIN  $db->data TEAM ON (TEAM.field_name = 'Team' AND TEAM.sub_id = FN.sub_id)
JOIN  $db->data DOB ON (DOB.field_name = 'DOB' AND DOB.sub_id = FN.sub_id)
JOIN  $db->data EMAIL ON (EMAIL.field_name = 'EMail' AND EMAIL.sub_id = FN.sub_id)
JOIN  $db->data PHONE ON (PHONE.field_name = 'Telephone' AND PHONE.sub_id = FN.sub_id)
WHERE FN.field_name = 'First Name'
ORDER BY LN.field_val

По сути, многие утомительные самостоятельные объединения - это цена, которую вы платите за эту "гибкую" организацию таблицы как набора имен и значений атрибутов.

Кстати, если некоторые данные могут отсутствовать для определенного sub_id, и вы все еще хотите видеть эту строку в выводе (с NULL для отсутствующих данных), используйте LEFT JOIN вместо простого JOIN для экземпляра этого поля данные в приведенном выше запросе.

2 голосов
/ 16 июня 2009

Если это возможно, я бы попытался изменить схему вашей базы данных и создать таблицу Person со всеми этими атрибутами (Имя, Фамилия, Команда и т. Д.); это было бы гораздо проще для тех, кто может позже захотеть сохранить это. Это сделало бы написание вашего запроса легким делом.

Конечно, я не знаю каких-либо дополнительных требований, которые могут у вас возникнуть. Я также предполагаю, что вы используете это в реляционной базе данных, которая говорит на SQL, а не в каком-либо другом типе базы данных.

1 голос
/ 16 июня 2009

Использовать фактическую схему базы данных. Подход с использованием «общей» схемы, которая может «содержать все», является абсолютно ужасной идеей. Удивительно, как часто этот подход становится «открытым» или «разрабатываемым» снова и снова «архитекторами».

Это часто описывается как EAV (entity-attribute-value) схема. Все, что вам нужно, это четыре стола, и тогда начинаются ваши страдания:

* objects
* attributes
* object_attributes (objects is 1:M with object_attributes)
* links (links objects to objects, an association table)
1 голос
/ 16 июня 2009
SELECT FN.sub_id, FN.field_val as `First Name`, LN.field_val as `Last Name`, DOB.field_val as `dob`, EMAIL.field_val as `email`, PHONE.field_val as `phone`
FROM       $db->data FN
INNER JOIN $db->data LN    on LN.sub_id    = FN.sub_id
INNER JOIN $db->data DOB   on DOB.sub_id   = FN.sub_id
INNER JOIN $db->data EMAIL on EMAIL.sub_id = FN.sub_id
INNER JOIN $db->data PHONE on PHONE.sub_id = FN.sub_id
WHERE FN.field_name    = 'First Name'
AND   LN.field_name    = 'Last Name'
AND   DOB.field_name   = 'DOB'
AND   EMAIL.field_name = 'EMail'
AND   PHONE.field_name = 'Telephone';

(просто демонстрирует хорошую идею Алекса Мартелли).

0 голосов
/ 16 июня 2009

Это глупый макет для такого рода данных.

Чтобы ответить на ваш вопрос, хотя:

Присоедините таблицу к себе под разными псевдонимами.

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

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

$querystr = "
  SELECT
    fname.sub_id
   ,fname.field_val as 'First Name'
   ,lname.field_val as 'Last Name'
   ,team.field_val as 'Team'
   ,dob.field_val as 'DOB'
   ,email.field_val as 'Email'
   ,phone.field_val as 'Phone'
  FROM  
    $db->data fname
   ,$db->data lname
   ,$db->data team
   ,$db->data dob
   ,$db->data email
   ,$db->data phone
  where fname.sub_id = lname.sub_id
    and fname.sub_id = team.sub_id
    and fname.sub_id = dob.sub_id
    and fname.sub_id = email.sub_id
    and fname.sub_id = phone.sub_id
    and fname.field_name = 'First Name'
    and lname.field_name = 'Last Name'
    and team.field_name = 'Team'
    and dob.field_name = 'DOB'
    and email.field_name = 'EMail'
    and phone.field_name = 'Telephone'  
  order by
    ln.field_val
";
0 голосов
/ 16 июня 2009

Опция-1: измените схему БД, чтобы иметь атрибуты в меньше динамических Таблица персон.

Вариант-2: Ваш код будет работать ТОЛЬКО в том случае, если у вас есть данные для всех полей. В противном случае он просто не вернет вам строку. Так что не только для READABILITY, но и для его работы вам нужно использовать LEFT JOINs. Но я бы действительно пошел на вариант-1.

Вариант 3: Почему бы вам просто не вернуть все из SQL в ваш код (ВЫБРАТЬ * из таблицы), а затем преобразовать его в ваши объекты на более удобном для вас языке программирования.

0 голосов
/ 16 июня 2009

Как и в случае с sheepsimulator, я настоятельно рекомендую обновить модель данных, где каждое поле на самом деле является собственным столбцом . В противном случае, если ваша СУБД поддерживает это, использование таких подвыборов может быть более быстрым, особенно если вы фактически ограничиваете набор результатов предложением where в конце:

select
    s.field_val as `First Name`,
    (select field_val from $db->data as i where field_name = 'Last Name' and i.sub_id = s.sub_id) as `Last Name`,
    (select field_val from $db->data as i where field_name = 'Team' and i.sub_id = s.sub_id) as `Team`,
    (select field_val from $db->data as i where field_name = 'DOB' and i.sub_id = s.sub_id) as `DOB`,
    (select field_val from $db->data as i where field_name = 'EMail' and i.sub_id = s.sub_id) as `E-Mail`,
    (select field_val from $db->data as i where field_name = 'Telephone' and i.sub_id = s.sub_id) as `Phone`,
from $db->data as s
order by `Last Name`
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...