Например, скажем, я хочу получить пользователя и все его номера телефонов и адреса электронной почты. Телефонные номера и электронные письма хранятся в отдельных таблицах, от одного пользователя до многих телефонов / электронных писем. Я могу сделать это довольно легко:
SELECT * FROM users user
LEFT JOIN emails email ON email.user_id=user.id
LEFT JOIN phones phone ON phone.user_id=user.id
Проблема * в этом заключается в том, что он возвращает имя пользователя, DOB, любимый цвет и всю другую информацию, хранящуюся в таблице пользователей, снова и снова для каждой записи (пользователи отправляют записи по электронной почте на телефоны), предположительно потребляя пропускную способность и замедляя вниз результаты.
Разве не было бы лучше, если бы он возвращал одну строку для каждого пользователя, и в этой записи был список электронных писем и список телефонов? Было бы намного проще работать с данными.
Я знаю, что вы можете получить такие результаты, используя LINQ или, возможно, другие фреймворки, но, похоже, это слабое место в структуре реляционных баз данных.
Мы могли бы обойти это, используя NoSQL, но разве не должно быть никакого среднего уровня?
Я что-то пропустил? Почему этого не существует?
* Да, он разработан таким образом. Я понял Мне интересно, почему нет альтернативы, с которой легче работать. SQL может продолжать делать то, что делает, но затем они могут добавить ключевое слово или два, чтобы выполнить небольшую постобработку, которая возвращает данные в вложенном формате вместо декартового произведения.
Я знаю, что это можно сделать на языке сценариев по вашему выбору, но для этого требуется, чтобы сервер SQL отправлял избыточные данные (пример ниже) или чтобы вы выполняли несколько запросов, например SELECT email FROM emails WHERE user_id IN (/* result of first query */)
.
Вместо того, чтобы MySQL возвращал что-то похожее на это:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "johnsmith45@gmail.com",
},
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"email": "john@smithsunite.com",
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"email": "originaljane@deerclan.com",
}
]
А затем необходимо сгруппировать некоторый уникальный идентификатор (а это значит, что мне тоже нужно его получить!) На стороне клиента, чтобы переформатировать набор результатов так, как вы хотите, просто верните это:
[
{
"name": "John Smith",
"dob": "1945-05-13",
"fav_color": "red",
"emails": ["johnsmith45@gmail.com", "john@smithsunite.com"]
},
{
"name": "Jane Doe",
"dob": "1953-02-19",
"fav_color": "green",
"emails": ["originaljane@deerclan.com"],
}
]
В качестве альтернативы я могу выдать 3 запроса: 1 для пользователей, 1 для электронных писем и 1 для телефонных номеров, но тогда в результирующих наборах электронной почты и телефонных номеров должен быть указан user_id, чтобы я мог сопоставить их с пользователями Я ранее принес. Опять же, избыточные данные и ненужная постобработка.
Ответы:
В глубине души в реляционной базе данных все строки и столбцы. Это структура, с которой оптимизирована реляционная база данных. Курсоры работают с отдельными строками одновременно. Некоторые операции создают временные таблицы (опять же, это должны быть строки и столбцы).
Работая только с строками и возвращая только строки, система может лучше справляться с памятью и сетевым трафиком.
Как уже упоминалось, это позволяет выполнять определенные оптимизации (индексы, объединения, объединения и т. Д.)
Если нужно хотеть вложенную древовидную структуру, это требует, чтобы каждый извлекал все данные одновременно. Прошли оптимизации для курсоров на стороне базы данных. Аналогично, трафик по сети становится одним большим взрывом, который может занять гораздо больше времени, чем медленный поток строк за строкой (это то, что иногда теряется в современном мире Интернета).
В каждом языке есть массивы. Это простые вещи для работы и взаимодействия. Используя очень примитивную структуру, драйвер между базой данных и программой - независимо от того, на каком языке - может работать обычным образом. Как только начинаются добавления деревьев, структуры в языке становятся более сложными и более сложными для прохождения.
Для языка программирования не так сложно преобразовать строки, возвращенные в какую-то другую структуру. Сделайте его в виде дерева или хеш-набора или оставьте его в виде списка строк, по которым вы можете перебирать.
Здесь также работает история. Передача структурированных данных была некрасивой в старые времена. Посмотрите на формат EDI, чтобы получить представление о том, что вы можете просить. Деревья также подразумевают рекурсию - которую некоторые языки не поддерживали (два самых важных языка прежних времен не поддерживали рекурсию - рекурсия не вошла в Фортран до F90 и той эпохи, которую COBOL тоже не поддерживал ).
И хотя современные языки поддерживают рекурсию и более продвинутые типы данных, на самом деле нет веских причин что-либо менять. Они работают, и они работают хорошо. Те, которые меняются вещи являются NoSQL базы данных. Вы можете хранить деревья в документах в документе. LDAP (на самом деле он старый) также является системой на основе дерева (хотя, вероятно, это не то, что вам нужно). Кто знает, может быть, следующая вещь в базах данных nosql будет возвращать запрос в виде объекта json.
Тем не менее, «старые» реляционные базы данных ... они работают со строками, потому что это то, в чем они хороши, и все может общаться с ними без проблем или перевода.
Из RFC 1925 - Двенадцать сетевых истин
источник
Он возвращает именно то, что вы просили: один набор записей, содержащий декартово произведение, определенное объединениями. Существует множество допустимых сценариев, в которых это именно то, что вы хотели бы, так что утверждение о том, что SQL дает плохой результат (и, следовательно, подразумевает, что было бы лучше, если бы вы его изменили), фактически приводило бы к большому количеству запросов.
То, что вы испытываете, известно как « несоответствие объекта / реляционного импеданса », технические трудности, возникающие из-за того, что объектно-ориентированная модель данных и реляционная модель данных принципиально различаются по нескольким причинам. LINQ и другие фреймворки (известные как ORM, Object / Relational Mappers, не случайно) не волшебным образом "обходят это"; они просто выдают разные запросы. Это можно сделать и в SQL. Вот как я это сделаю:
Переберите список пользователей и составьте список идентификаторов.
И тогда вы делаете присоединение на стороне клиента. Вот как это делают LINQ и другие фреймворки. Здесь нет настоящей магии; просто слой абстракции.
источник
Вы можете использовать встроенную функцию для объединения записей вместе. В MySQL вы можете использовать
GROUP_CONCAT()
функцию, а в Oracle вы можете использоватьLISTAGG()
функцию.Вот пример того, как запрос может выглядеть в MySQL:
Это вернуло бы что-то вроде
источник
Проблема в том, что вы недостаточно избирательны. Вы просили все, когда вы сказали
... и вы получили его (включая DOB и любимые цвета).
Вы, вероятно, должны быть немного более (хм) ... избирательно, и сказал что-то вроде:
Также возможно, что вы видите записи, которые выглядят как дубликаты, потому что a
user
может объединяться с несколькимиemail
записями, но поле, которое различает эти две, отсутствует в вашемSelect
утверждении, поэтому вы можете сказать что-то вродеКроме того, я заметил, что вы делаете
LEFT JOIN
. Это объединит все записи слева от объединения (т. Е.users
) Со всеми записями справа или другими словами:( http://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join )
Итак, другой вопрос: вам действительно нужно левое соединение, или было
INNER JOIN
бы достаточно? Это очень разные типы соединений.Если вы действительно хотите, чтобы один столбец в наборе результатов содержал список, который генерируется на лету, это можно сделать, но это зависит от используемой базы данных. Oracle имеет
listagg
функцию .В конечном счете, я думаю, что ваша проблема может быть решена, если вы перепишете свой запрос примерно так:
источник
left join
наinner join
. В этом случае это не уменьшит количество повторений, на которые жалуется пользователь; он просто пропустит тех пользователей, у которых нет телефона или электронной почты. вряд ли улучшения. Кроме того, при интерпретации «все записи слева ко всем записям справа» пропускаютсяON
критерии, которые исключают все «неправильные» отношения, присущие декартовому произведению, но сохраняют все повторяющиеся поля.Запросы всегда производят прямоугольный (не зазубренный) табличный набор данных. В наборе нет вложенных подмножеств. В мире множеств все является чистым не вложенным прямоугольником.
Вы можете думать о соединении как о размещении 2 комплектов бок о бок. Условие «включено» - это сопоставление записей в каждом наборе. Если у пользователя есть 3 телефонных номера, вы увидите трехкратное дублирование в информации о пользователе. По запросу должен быть получен прямоугольный набор без зазубрин. Это просто природа объединения наборов с отношением 1-ко-многим.
Чтобы получить то, что вы хотите, вы должны использовать отдельный запрос, как описал Мейсон Уилер.
Результатом этого запроса по-прежнему остается прямоугольный набор без зазубрин. Как и все в мире наборов.
источник
Вы должны решить, где существуют узкие места. Пропускная способность между вашей базой данных и приложением обычно довольно высокая. Нет причин, по которым большинство баз данных не могли бы вернуть 3 отдельных набора данных за один вызов и без объединений. Затем вы можете объединить все это вместе в своем приложении, если хотите.
В противном случае вы хотите, чтобы база данных объединила этот набор данных, а затем удалила все повторяющиеся значения в каждой строке, которые являются результатом объединений, и не обязательно сами строки имеют дублирующиеся данные, например, два человека с одинаковым именем или номером телефона. Похоже, много накладных расходов, чтобы сэкономить на пропускной способности. Вам лучше сосредоточиться на возвращении меньшего количества данных с лучшей фильтрацией и удалением ненужных столбцов. Потому что Select * никогда не используется в производстве - это зависит от ситуации.
источник
Проще говоря, не объединяйте свои данные, если вам нужны разные результаты для запроса пользователя и запроса телефонного номера, в противном случае, как отмечают другие, «Установить» или данные будут содержать дополнительные поля для каждой строки.
Выпустите 2 различных запроса вместо одного с объединением.
В хранимой процедуре или параметризованном встроенном sql обработайте 2 запроса и верните результаты обоих обратно. Большинство баз данных и языков поддерживают несколько наборов результатов.
Например, SQL Server и C # выполняют эту функцию с помощью
IDataReader.NextResult()
.источник
Вы что-то упустили. Если вы хотите денормализовать ваши данные, вы должны сделать это самостоятельно.
источник
Концепция реляционного замыкания в основном означает, что результатом любого запроса является отношение, которое может использоваться в других запросах, как если бы это была базовая таблица. Это мощная концепция, потому что она делает запросы составными.
Если бы SQL позволял вам писать запросы, которые выводили бы вложенные структуры данных, вы бы нарушили этот принцип. Вложенная структура данных не является отношением, поэтому вам потребуется новый язык запросов или сложные расширения для SQL, чтобы запросить его дальше или присоединить к нему другие отношения.
По сути, вы должны построить иерархическую СУБД поверх реляционной СУБД. Это будет гораздо сложнее для сомнительной выгоды, и вы потеряете преимущества последовательно реляционной системы.
Я понимаю, почему иногда было бы удобно иметь возможность выводить иерархически структурированные данные из SQL, но затраты на дополнительную сложность во всей СУБД для поддержки этого определенно не стоят того.
источник
Pls относится к использованию функции STUFF, которая группирует несколько строк (телефонных номеров) столбца (контакта), которые могут быть извлечены как единая ячейка значений строки с разделителями (пользователь).
Сегодня мы широко используем это, но сталкиваемся с некоторыми проблемами с процессором и производительностью. Тип данных XML - это еще один вариант, но это изменение дизайна, а не уровень запроса.
источник
STUFF
это похоже на сращивание. Не уверен, как это относится к моему вопросу.