2010-11-17 2 views
7

Я считаю себя достаточно компетентным в понимании и управлении C-иш-языками; для меня не проблема придумать алгоритм и реализовать его на любом языке C-ish.Что такое структурированный способ построения запроса MySQL?

У меня огромная сложность при написании запросов SQL (в моем конкретном случае, MySQL). Для очень простых запросов это не проблема, но для сложных запросов я расстраиваюсь, не зная, с чего начать. Чтение документации MySQL затруднено, главным образом потому, что описание и объяснение синтаксиса не организованы очень хорошо.

Например, документация SELECT все по карте: она начинается с того, что выглядит как псевдо-BNF, но потом (так как текст для агрегированных описаний не кликабельны ... как select_expr) он быстро переходит в это разочаровывающее упражнение в попытке собрать синтаксис вместе, открыв несколько окон браузера.

Достаточно скулить.

Хотелось бы узнать, как люди шаг за шагом начинают строить сложный запрос MySQL. Вот пример. У меня три таблицы ниже. Я хочу SELECT набор строк со следующими характеристиками:

Из таблиц userInfo и userProgram, я хочу, чтобы выбрать userName, isApproved и modifiedTimestamp полей и UNION их в один набор. Из этого набора я хочу, чтобы ORDERmodifiedTimestamp принимал MAX(modifiedTimestamp) для каждого пользователя (т. Е. Должна быть только одна строка с уникальным userName, а временная метка, связанная с этим именем пользователя, должна быть как можно выше).

Из user таблицы, я хочу, чтобы соответствовать firstName и lastName, что связано с userName так, что он выглядит примерно так:

+-----------+----------+----------+-------------------+ 
| firstName | lastName | userName | modifiedTimestamp | 
+-----------+----------+----------+-------------------+ 
| JJ  | Prof  | jjprofUs |  1289914725 | 
| User  | 2  | user2 |  1289914722 | 
| User  | 1  | user1 |  1289914716 | 
| User  | 3  | user3 |  1289914713 | 
| User  | 4  | user4 |  1289914712 | 
| User  | 5  | user5 |  1289914711 | 
+-----------+----------+----------+-------------------+ 

Ближайший я получил запрос, который выглядит это:

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userInfo 
WHERE user.userName=userInfo.userName) 

UNION 

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userProgram 
WHERE user.userName=userProgram.userName) 

ORDER BY modifiedTimestamp DESC; 

Я чувствую, что я довольно близко, но я не знаю, куда идти отсюда, или даже если я думаю об этом в правильном направлении.

> user 
+--------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+--------------------+--------------+------+-----+---------+-------+ 
| userName   | char(8)  | NO | PRI | NULL |  | 
| firstName   | varchar(255) | NO |  | NULL |  | 
| lastName   | varchar(255) | NO |  | NULL |  | 
| email    | varchar(255) | NO | UNI | NULL |  | 
| avatar    | varchar(255) | YES |  | ''  |  | 
| password   | varchar(255) | NO |  | NULL |  | 
| passwordHint  | text   | YES |  | NULL |  | 
| access    | int(11)  | NO |  | 1  |  | 
| lastLoginTimestamp | int(11)  | NO |  | -1  |  | 
| isActive   | tinyint(4) | NO |  | 1  |  | 
+--------------------+--------------+------+-----+---------+-------+ 

> userInfo 
+-------------------+------------+------+-----+---------+-------+ 
| Field    | Type  | Null | Key | Default | Extra | 
+-------------------+------------+------+-----+---------+-------+ 
| userName   | char(8) | NO | MUL | NULL |  | 
| isApproved  | tinyint(4) | NO |  | 0  |  | 
| modifiedTimestamp | int(11) | NO |  | NULL |  | 
| field    | char(255) | YES |  | NULL |  | 
| value    | text  | YES |  | NULL |  | 
+-------------------+------------+------+-----+---------+-------+ 

> userProgram 
+-------------------+--------------+------+-----+---------+-------+ 
| Field    | Type   | Null | Key | Default | Extra | 
+-------------------+--------------+------+-----+---------+-------+ 
| userName   | char(8)  | NO | PRI | NULL |  | 
| isApproved  | tinyint(4) | NO | PRI | 0  |  | 
| modifiedTimestamp | int(11)  | NO |  | NULL |  | 
| name    | varchar(255) | YES |  | NULL |  | 
| address1   | varchar(255) | YES |  | NULL |  | 
| address2   | varchar(255) | YES |  | NULL |  | 
| city    | varchar(50) | YES |  | NULL |  | 
| state    | char(2)  | YES | MUL | NULL |  | 
| zip    | char(10)  | YES |  | NULL |  | 
| phone    | varchar(25) | YES |  | NULL |  | 
| fax    | varchar(25) | YES |  | NULL |  | 
| ehsChildren  | int(11)  | YES |  | NULL |  | 
| hsChildren  | int(11)  | YES |  | NULL |  | 
| siteCount   | int(11)  | YES |  | NULL |  | 
| staffCount  | int(11)  | YES |  | NULL |  | 
| grantee   | varchar(255) | YES |  | NULL |  | 
| programType  | varchar(255) | YES |  | NULL |  | 
| additional  | text   | YES |  | NULL |  | 
+-------------------+--------------+------+-----+---------+-------+ 
+1

Это абсолютно не связано с jQuery. Удаленный тег. – casablanca

+0

Если вы хотите изучить SQL, документация по реализации базы данных, использующая ее, не является хорошей литературой. Документация написана для тех, кто уже хорошо знает SQL. Итак, вы должны найти некоторую литературу для изучения SQL. – Guffa

ответ

1

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

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userInfo ui1 
WHERE user.userName=userInfo.userName 
AND modifiedtimestamp=(select max(modifiedtimestamp) from userInfo ui2 where ui1.userName=ui2.userName)) 

UNION 

(SELECT firstName, lastName, user.userName, modifiedTimestamp 
FROM user, userProgram up1 
WHERE user.userName=userProgram.userName 
AND modifiedtimestamp=(select max(modifiedtimestamp) from userProgram up2 where up1.userName=up2.userName)) 
ORDER BY modifiedTimestamp DESC; 

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

Шаг 1: Выберите поля, которые мне нужны, в разных таблицах моей базы данных. Это то, что находится между SELECT и FROM. Кажется очевидным, но он становится менее очевидным, когда речь идет о функции агрегации, например, суммы или счета. В этом случае вы должны сказать, например: «Мне нужно подсчет строк в userInfo для каждого firstName». См. Ниже в разделе GROUP BY.

Шаг 2: Зная поле, которое вам нужно, напишите соединения между различными соответствующими таблицами. Это легко ...

Шаг 3: Выразите свои условия.Это может быть легко, например, если вам нужны данные от пользователя для userName = «RZEZDFGBH» или более сложные, как в вашем случае: способ сформулировать его, чтобы вы могли это сделать, если хотите только самую последнюю измененную метку, «так, чтобы измененная метка была равна самой последней измененной типом» (здесь вы можете легко взять умственный ярлык и пропустить точку)

Шаг 4: Если у вас есть агрегаты, пришло время установить оператор GROUP BY. Например, если считать все строки в USERINFO для каждого ПгвЬЫате, можно было бы написать «GROUP BY FirstName»:

SELECT firstName,count(*) FROM userInfo GROUP BY firstName 

Это дает количество записей в таблице для каждого отдельного ПгвЬЫате.

Этап 5: ИМЕЮТ УСЛОВИЯ. Это условия для агрегатов. В предыдущем примере, если вам нужны только данные для firstName, имеющие более 5 строк в таблице, вы можете написать SELECT firstName,count(*) FROM userInfo GROUP BY firstName HAVING count(*)>5

Шаг 6: Сортировка с помощью ORDER BY. Довольно легко ...

Это только краткое резюме. Существует много, гораздо больше, чтобы обнаружить, но было бы слишком долго писать полный курс SQL здесь ... Надеюсь, что это поможет!

+0

Это не отвечает на мой вопрос. Меня не так интересует ответ на мой пример. Я заинтересован в том, чтобы вы пришли к этому ответу. Если бы вы могли посмотреть на мое описание, а затем показать мне мыслительный процесс для построения запроса, это было бы потрясающе. – Avery

+0

Проблема в том, что я не совсем уверен, что мой код делает то, что вы хотите, потому что ваш вопрос немного неясен. Предположим, что это так и есть то, что вы ищете, начиная с результата вашего запроса, возвращая только строку, соответствующую последней версии modifiedTimestamp для каждого пользователя. Это верно? Я изменю свой ответ. –

0

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

В последнем пункте ... редко бывает, что вы хотите, чтобы объединение временных меток указывало на несколько источников. Вообще говоря, когда такие результаты собираются, это обычно используется для какого-то одитинга/трассировки. Однако, когда вы отбрасываете всю информацию об источнике временной метки и просто вычисляете максимум, который у вас есть ... ну что конкретно?

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

В той мере, я буду делать какие-либо предсказания о форме своего возможного заявления (если ваша задача будет по-прежнему, чтобы получить один максимальную отметку времени для каждого пользователя) это то, что это будет выглядеть примерно так:

select u.firstname, u.lastname, user_max_time.userName, user_max_time.max_time 
from users u, 
(select (sometable).userName, max((sometable).(timestamp column)) 
from (data of interest) 
group by (sometable).userName) user_max_time 
where u.userName = user_max_time.userName 
order by max_time desc; 

Ваша задача здесь состояла в том, чтобы заменить() s внутри подзапроса user_max_time на то, что имеет смысл и соответствует вашим требованиям. Что касается общего подхода к сложному sql, основным предложением является построение запроса из самых внутренних подзапросов назад (тестирование по пути, чтобы убедиться, что производительность в порядке, и вам не нужны промежуточные таблицы).

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

Приветствия, Бен

+0

вы можете форматировать код на SO, выделив его и нажав кнопку «101». –

+0

Да, я понимаю, что значимые структуры таблиц, которые я написал, непрозрачны. Просто чтобы принести немного больше света: для нашего приложения мы всегда будем иметь userProgram, но элементы в userInfo являются гибкими. Причина, по которой мы объединяем их, заключается в том, что мы хотим, чтобы последнее обновление произошло, будь то совокупность userProgram или элемент в userInfo. – Avery

1

Как говорит f00, это просто (г), если вы думаете о данных в терминах множеств.

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

Это иллюстрирует то, что первый шаг при написании запроса состоит в том, чтобы иметь ясное представление о , что, которого вы хотите достичь. Большая проблема с этим вопросом заключается в том, что это не ясно описано - вместо этого оно перемещается из примерной таблицы ожидаемого результата (что было бы более полезно, если бы у нас были соответствующие выборки ожидаемых входных данных) прямо в описании как вы намерены достичь этого.

Как я понимаю, , что вы хотите увидеть это список пользователей (по имени пользователя, связанные с ними и фамилии), вместе с последним временем все связанные записи был изменен либо на USERINFO или userProgram столы.

(Не ясно, хотите ли вы видеть пользователей, которые не имеют ассоциированную активность на любом из этих таблиц - ваш поставляется запрос подразумевает не так, иначе стыки бы внешние соединения.)

Таким образом, вы нужен список пользователей (по имени пользователя, с их ассоциированными именами и фамилиями):

SELECT firstName, lastName, userName 
FROM user 

вместе со списком раз, что записи последнего изменения:

SELECT userName, MAX(modifiedTimestamp) 

...

либо на USERINFO или userProgram таблицы:

...

FROM 
(SELECT userName, modifiedTimestamp FROM userInfo 
UNION ALL 
SELECT userName, modifiedTimestamp FROM userProgram 
) subquery -- <- this is an alias 

...

по имени пользователя:

...

group by userName 

Эти два набора данных должны быть связаны их именем пользователя - поэтому окончательный запрос становится:

SELECT user.firstName, user.lastName, user.userName, 
     MAX(subquery.modifiedTimestamp) last_modifiedTimestamp 
FROM user 
JOIN 
(SELECT userName, modifiedTimestamp FROM userInfo 
UNION ALL 
SELECT userName, modifiedTimestamp FROM userProgram 
) subquery 
ON user.userName = subquery.userName 
GROUP BY user.userName 

В большинстве версий SQL, этот запрос будет возвращать ошибку в user.firstName и user.lastName не включены в п GROUP BY , и они не суммируются. MySQL допускает этот синтаксис - в других SQL, поскольку эти поля функционально зависят от имени пользователя, добавляя MAX перед каждым полем или, добавляя их в группу, чтобы достичь того же результата.

Несколько дополнительных пунктов:

  • UNION и UNION ALL не идентичны - бывший удаляет дубликаты в то время как последний не делает; это делает первый процессор более интенсивным. Поскольку дубликаты будут удалены группировкой, лучше использовать UNION ALL.
  • Многие люди напишут этот запрос, когда пользователь присоединился к userInfo UNIONed ALL с пользователем, подключенным к userProgram - это связано с тем, что многие SQL-модули могут более эффективно оптимизировать этот тип запросов. На данный момент это представляет собой преждевременную оптимизацию.
1

Здесь много хорошего. Спасибо всем, кто внес свой вклад. Это краткое изложение того, что я нашел полезным, а также некоторые дополнительные мысли о подключении функций здания к построению запросов. Мне жаль, что я не могу дать всем СО-значки/баллы, но я думаю, что может быть только один (ответ), поэтому я выбираю Трарот на основе общей точки и личной полезности.

Функция может быть понята как три части: вход, процесс, выход. Аналогичный запрос можно понять. Большинство запросов выглядеть примерно так:

SELECT stuff FROM data WHERE data is like something 
  • SELECT часть является выходным. Существуют некоторые возможности для форматирования вывода здесь (например, с использованием AS)

  • Часть FROM - это вход. Ввод следует рассматривать как пул данных; вы захотите сделать это как можно более конкретным, используя различные соединения и подзапросы, которые являются подходящими.

  • Часть WHERE как процесс, но есть много перекрытий с частью FROM. Как части FROM, так и WHERE могут уменьшить пул данных соответствующим образом, используя различные условия для фильтрации нежелательных данных (или только для ввода желаемых данных). Часть WHERE также может помочь в форматировании вывода.

Вот как я сломалась шаги:

  1. Начните с мышления о том, что ваш вывод выглядит. Этот материал входит в часть SELECT.

  2. Далее вы хотите определить набор данных, над которыми хотите работать. Заметки: «Зная поле, которое вам нужно, напишите соединения между разными соответствующими таблицами. Это легко ...» Это зависит от того, что вы подразумеваете под «легким». Если вы новичок в написании запросов, вы, вероятно, просто по умолчанию написали внутренние объединения (как и я). Это не всегда лучший способ. http://en.wikipedia.org/wiki/Join_(SQL) - отличный ресурс для понимания различных возможных объединений.

  3. В качестве части предыдущего шага подумайте о небольших частях этого набора данных и создайте полный набор данных, который вас интересует. При написании функции вы можете написать подфункции, чтобы помочь выразить ваш процесс в более ясном манера. Аналогично этому вы можете писать подзапросы. Огромный совет от Марка Баннистера в создании подзапроса И ИСПОЛЬЗОВАНИЕ АЛИАЗА. Вам нужно будет перенастроить свой вывод, чтобы использовать этот псевдоним, но это довольно важно.

  4. Наконец, вы можете использовать различные методы, чтобы урезать свой набор данных, удаление данных вы не заинтересованы в

Один способ думать о данных, вы работаете на гигантский 2- D: JOIN s сделать больший горизонтальный аспект, UNION s сделать увеличенный вертикальный аспект.Все остальные фильтры предназначены для того, чтобы сделать эту матрицу более подходящей для вашего вывода. Я не знаю, есть ли «функциональная» аналогия с JOIN, но UNION просто добавляет вывод двух функций вместе.

Я понимаю, что существует множество способов, с помощью которых запрос на строительство НЕ нравится писать функцию. Например, вы можете создавать и парировать набор данных в областях FROM и WHERE. Что для меня было ключевым, было понимание объединения и выяснение того, как создавать подзапросы с использованием псевдонимов.

Смежные вопросы