Почему реляционные базы данных не поддерживают возврат информации во вложенном формате?

46

Предположим, я создаю блог, в котором я хочу иметь посты и комментарии. Поэтому я создаю две таблицы: таблицу «posts» с автоинкрементным целочисленным столбцом «id» и таблицу «comments» с внешним ключом «post_id».

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

SELECT id, content, (SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7

Который дал бы мне идентификатор и содержание сообщения, которое я хочу, вместе со всеми соответствующими строками комментариев, аккуратно упакованными в массив (вложенное представление, которое вы использовали бы в JSON). Конечно, SQL и реляционные базы данных не работают таким образом, и самое близкое, что они могут получить, это объединить «посты» и «комментарии», которые будут возвращать много ненужного дублирования данных (с повторением одной и той же информации поста). в каждой строке), что означает, что время обработки тратится как на базу данных, чтобы собрать все это вместе, так и на мой ORM, чтобы проанализировать и отменить все это.

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

Я понимаю, что реляционные базы данных являются проверенной технологией (черт, они старше меня), и что за эти десятилетия в них было проведено множество исследований, и я уверен, что есть действительно веская причина, почему они (и Стандарт SQL) предназначены для того, чтобы функционировать так, как они, но я не уверен, почему описанный выше подход невозможен. Мне кажется, это самый простой и очевидный способ реализации одного из самых основных отношений между записями. Почему реляционные базы данных не предлагают что-то подобное?

(Отказ от ответственности: я в основном пишу веб-приложения с использованием хранилищ данных Rails и NoSQL, но недавно я пробовал Postgres, и мне это очень нравится. Я не хочу атаковать реляционные базы данных, я просто озадачен.)

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

PreciousBodilyFluids
источник
1
не все формы работают таким образом. hibernate / nhibernate позволяет указывать объединения и может загружать целые деревья объектов из одного запроса.
Натан Гонсалес
1
Кроме того, хотя это интересная тема для обсуждения, я не уверен, что это действительно ответственно без встречи с ребятами ansi sql
Натан Гонсалес
@nathan: Да, не все. Я использовал Sequel, который позволяет вам выбрать, какой подход вы предпочитаете для данного запроса ( документы ), но они все же поддерживают подход с несколькими запросами (я полагаю, из соображений производительности).
5
Поскольку СУБД предназначена для хранения и извлечения наборов, она не предназначена для возврата данных для отображения. Думайте об этом как о MVC - зачем пытаться реализовать представление за счет замедления или усложнения модели? СУБД предлагают преимущества, которых нет в базах данных NoSQL (и наоборот) - если вы используете их, потому что это правильный инструмент для решения вашей проблемы, вы не попросите его вернуть данные, готовые для отображения.
1
Они видят для xml
Ян

Ответы:

42

CJ Date подробно рассказывает об этом в главе 7 и приложении B к SQL и реляционной теории . Вы правы, в реляционной теории нет ничего, что запрещало бы типу данных атрибута быть самим отношением, если только он имеет одинаковый тип отношения в каждой строке. Ваш пример будет соответствовать.

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

Запросы, ограничения и обновления являются более сложными, сложными для написания и более сложными для поддержки СУБД, если вы разрешите атрибуты со значениями отношений (RVA).

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

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

Из того, что я понимаю (я не использовал их), Rel и Dataphor являются проектами СУБД, которые поддерживают атрибуты, имеющие отношение к значениям.


Re комментарий от @dportas:

Структурированные типы являются частью SQL-99, и Oracle поддерживает их. Но они не хранят несколько кортежей во вложенной таблице на строку базовой таблицы. Типичным примером является атрибут «адрес», который выглядит как один столбец базовой таблицы, но имеет дополнительные подколонки для улицы, города, почтового индекса и т. Д.

Вложенные таблицы также поддерживаются Oracle, и они допускают несколько кортежей на строку базовой таблицы. Но я не знаю, что это является частью стандартного SQL. И имейте в виду вывод одного блога: «Я никогда не буду использовать вложенную таблицу в выражении CREATE TABLE. Вы тратите все свое время, НЕ ВСТАВЛЯЯ их, чтобы снова сделать их полезными!»

Билл Карвин
источник
3
Я бы не хотел хранить одно отношение внутри другого - они были бы в отдельных таблицах и денормализованы как обычно. Я просто спрашиваю, почему такого рода вложение результатов не допускается в запросах, когда оно кажется мне более интуитивным, чем модель объединения.
PreciousBodilyFluids
Наборы результатов и таблицы в своем роде. Date называет их отношениями и relvars соответственно (по аналогии, 42 является целым числом, тогда как переменная xможет иметь значение целого числа 42). Те же самые операции применяются к отношениям и relvars, поэтому их структура должна быть совместимой.
Билл Карвин
2
Стандартный SQL поддерживает вложенные таблицы. Они называются «структурированными типами». Oracle - это одна СУБД, которая имеет эту функцию.
nvogel
2
Разве не абсурдно утверждать, что во избежание дублирования данных вы должны написать свой запрос простым, дублирующим данные способом?
Имон Нербонн
1
@EamonNerbonne, симметрия реляционных операций. Например, проекция. Если я ВЫБИРАЮ некоторые субатрибуты из RVA, как я могу применить обратную операцию к результирующему набору для воспроизведения исходной иерархии? Я обнаружил, что страница 293 книги Дэйна находится в Google Книгах, поэтому вы можете видеть, что он написал: books.google.com/…
Билл Карвин,
15

Некоторые из самых ранних систем баз данных были основаны на модели иерархической базы данных . Эти данные представлены в виде древовидной структуры с родителями и детьми, так же, как вы предлагаете здесь. HDMS были в значительной степени заменены базами данных, построенными на реляционной модели. Основными причинами этого было то, что RDBMS могла моделировать отношения «многие ко многим», которые были трудны для иерархических баз данных, и что RDBMS могла легко выполнять запросы, которые не были частью первоначального проекта, тогда как HDBMS ограничивала вас запросом по путям, указанным во время разработки.

Есть все еще некоторые примеры иерархических систем баз данных в дикой природе, особенно реестр Windows и LDAP.

Обширное освещение этой темы доступно в следующей статье

Стив Уит
источник
10

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

Затем вы утверждаете, что «даже если я проинструктирую свой ORM с нетерпением загружать комментарии к записи, лучшее, что она сделает, - отправит один запрос к сообщению, а затем второй запрос, чтобы получить все комментарии, а затем соединить их вместе. на стороне клиента, что тоже неэффективно " .

Я не вижу ничего неэффективного в отправке 2 запросов и получении 2 пакетов результатов с:

--- Query-1-posts
SELECT id, content 
FROM posts
WHERE id = 7


--- Query-2-comments
SELECT * 
FROM comments 
WHERE post_id = 7

Я бы сказал, что это (почти) наиболее эффективный способ (почти, поскольку вам не нужны posts.idстолбцы, а не все comments.*)

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

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

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

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

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

ypercubeᵀᴹ
источник
1
Я имел в виду неэффективность в том смысле, что мое приложение должно понести издержки и задержку двух вызовов базы данных вместо одного. Кроме этого, разве объединение не просто возвращает данные в формате, который готов для отображения? Или используя представление базы данных? Вы также можете избавиться от них, просто запустив несколько небольших запросов и соединив их в своем приложении, если хотите, но они все еще являются полезными инструментами. Я не думаю, что то, что я предлагаю, значительно отличается от объединения, кроме того, что оно проще в использовании и более производительно.
2
@Precious: для выполнения нескольких запросов не нужно увеличивать накладные расходы. Большинство баз данных позволяют отправлять несколько запросов в одном пакете и получать несколько наборов результатов из одного запроса.
Даниэль Приден
@PreciousBodilyFluids - фрагмент SQL в ответе ypercube представляет собой один запрос, который будет отправлен за один вызов базы данных и вернет два набора результатов в одном ответе.
Carson63000
5

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

Стефано Борини
источник
5

Я знаю, что по крайней мере SQLServer поддерживает вложенные запросы при использовании FOR XML.

SELECT id, content, (SELECT * FROM comments WHERE post_id = posts.id FOR XML PATH('comments'), TYPE) AS comments
FROM posts
WHERE id = 7
FOR XML PATH('posts')

Проблема здесь не в отсутствии поддержки со стороны СУРБД, а в отсутствии поддержки вложенных таблиц в таблицах.

Кроме того, что мешает вам использовать внутреннее соединение?

SELECT id, content, comments.*
FROM posts inner join comments on comments.post_id = posts.id
WHERE id = 7

Фактически вы можете посмотреть на внутреннее объединение как на вложенную таблицу, только содержимое первых 2 полей может быть повторено в любое время. Я не стал бы сильно беспокоиться о производительности объединения, единственная медленная часть в запросе, подобном этому, - это ввод данных из базы данных в клиент. Это будет проблемой, только если контент содержит большой объем данных. В этом случае я бы предложил два запроса, один с select id, contentи один с внутренним соединением и select posts.id, comments.*. Это масштабируется даже с несколькими постами, так как вы все равно будете использовать только 2 запроса.

Dorus
источник
Вопросы обращаются к этому. Либо вам нужно совершить два обхода (не оптимально), либо вы должны возвращать избыточные данные в первых двух столбцах (также не оптимально). Он хочет оптимального решения (на мой взгляд, нереального).
Скотт Уитлок
Я знаю, но нет ничего плохого в качестве оптимального решения. Единственное, что я могу поспорить, это то, где накладные расходы будут минимальными и где они зависят. Если вы хотите оптимальное решение, сравните и попробуйте разные подходы. Даже решение XML может быть медленнее в зависимости от конкретной ситуации, и я незнаком с хранилищами данных NoSQL, поэтому я не могу сказать, есть ли в нем что-то подобное for xml.
Дорус
5

На самом деле Oracle поддерживает то, что вы хотите, но вам нужно заключить в подзапрос ключевое слово «cursor». Результаты выбираются через открытый курсор. Например, в Java комментарии будут отображаться как наборы результатов. Подробнее об этом см. Документацию Oracle "CURSOR Expression"

SELECT id, content, cursor(SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7
Дилшод Таджибаев
источник
1

Некоторые поддерживают вложение (иерархическое).

Если вам нужен один запрос, вы можете иметь одну таблицу, которая сама ссылается на себя. Некоторые RDMS поддерживают эту концепцию. Например, в SQL Server можно использовать общие табличные выражения (CTE) для иерархического запроса.

В вашем случае сообщения будут на уровне 0, а затем все комментарии будут на уровне 1.

Другими вариантами являются 2 запроса или соединение с некоторой дополнительной информацией для каждой возвращенной записи (которую упоминали другие).

Пример иерархического:

https://stackoverflow.com/questions/14274942/sql-server-cte-and-recursion-example

В приведенной выше ссылке EmpLevel показывает уровень вложенности (или иерархию).

Джон Рейнор
источник
Я не могу найти какую-либо документацию о подрезультатах в SQL Server. Даже при использовании CTE. Под набором результатов я подразумеваю строки данных с достаточным количеством строго типизированных столбцов. Можете ли вы добавить ссылки на ваш ответ?
SandRock
@SandRock - база данных отправит обратно один набор результатов из запроса SQL. Определив уровни в самом запросе, вы можете создать иерархический или вложенный набор результатов, который должен быть обработан. Я думаю, что в настоящее время мы наиболее близки к тому, чтобы возвращать вложенные данные.
Джон Рейнор
0

Извините, я не уверен, что точно понимаю вашу проблему.

В MSSQL вы можете просто выполнить 2 оператора SQL.

SELECT id, content
FROM posts
WHERE id = 7

SELECT * FROM comments WHERE post_id = 7

И он вернет ваши 2 набора результатов одновременно.

Biff MaGriff
источник
Человек, задающий вопрос, говорит, что это менее эффективно, потому что это приводит к двум обходам базы данных, и мы обычно стараемся свести к минимуму обходы из-за накладных расходов. Он хочет сделать один круг и вернуть оба стола.
Скотт Уитлок
Но это будет одна поездка туда и обратно. stackoverflow.com/questions/2336362/…
Biff MaGriff
0

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

Поскольку модель проста и опять-таки основана на теории, она позволяет людям легко оптимизировать и реализовать много реализаций. Это не похоже на NoSQL, где все делают это по-своему.

В прошлом предпринимались попытки создать иерархические базы данных, но у IIRC (похоже, нет, Google) возникли проблемы (на ум приходят циклы и равенство).

Адам Гент
источник
0

У вас есть конкретные потребности. Было бы предпочтительнее извлекать данные из базы данных в нужном вам формате, поэтому вы можете делать с ними все, что захотите.

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

Единственный аргумент, который я имею против вашего предложения - это возможность обработать этот набор результатов "sql" способом. Было бы плохой идеей создать результат в базе данных, если он не сможет работать с ним или манипулировать им до некоторой степени. Допустим, я создал представление, построенное так, как вы предлагаете, как мне включить его в другой оператор выбора? Базы данных любят получать результаты и делать что-то с ними. Как бы я присоединить его к другому столу? Как бы я сравнил ваш результат с другим?

Тогда выгода RDMS's - гибкость sql. Синтаксис для выбора данных из таблицы довольно близок к списку пользователей или других объектов в системе (по крайней мере, это цель). Не уверен, что есть смысл делать что-то совершенно другое. Они даже не довели их до такой степени, что обрабатывали процедурный код / ​​курсоры или BLOBS данных очень эффективно.

JeffO
источник
0

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

Конечно, PostgreSQL имеет некоторые особенности объектно-ориентированной базы данных. Согласно этому письму ( сообщению ) вы можете достичь того, что вам нужно, создав собственный агрегат.

Лично я использую фреймворки, такие как Doctrine ORM (PHP), которые выполняют агрегацию на стороне приложения и поддерживают такие функции, как ленивая загрузка, для повышения производительности.

Даймон
источник
0

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

Джонатан Роджерс
источник
1
это, кажется, не предлагает ничего существенного по сравнению с замечаниями, сделанными и объясненными в предыдущих 13 ответах
комнат
В вопросе конкретно упоминается JSON, и этот ответ является единственным, который указывает на то, что JSON может быть возвращен в запросах по крайней мере от одной СУБД. Я бы предпочел прокомментировать вопрос, чтобы сказать, что он основан на ложной предпосылке и поэтому не может ожидать какого-либо окончательного ответа. Однако StackExchange не позволит мне сделать это.
Джонатан Роджерс