관계형 데이터베이스가 중첩 형식으로 정보를 반환하는 것을 지원하지 않는 이유는 무엇입니까?


46

게시물과 댓글을 남기고 싶은 블로그를 만들고 있다고 가정합니다. 그래서 자동 증분 정수 'id'열이있는 'posts'테이블과 외래 키 'post_id'가있는 'comments'테이블의 두 테이블을 만듭니다.

그런 다음 가장 일반적인 쿼리 일 것입니다. 게시물과 모든 댓글을 검색하는 것입니다. 관계형 데이터베이스에 익숙하지 않은 나에게 가장 명백한 접근 방식은 다음과 같은 쿼리를 작성하는 것입니다.

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

어느 것이나 원하는 게시물의 ID와 내용을 배열에 깔끔하게 패키지 된 모든 관련 주석 행과 함께 제공합니다 (JSON에서 사용하는 것과 같은 중첩 표현). 물론, SQL 및 관계형 데이터베이스는 이와 같이 작동하지 않으며, 가장 가까운 것은 '포스트'와 '코멘트'를 결합하여 불필요한 데이터 중복을 반환하는 동일한 포스트 정보를 반복하는 것입니다. 모든 행에서), 이는 처리 시간이 모두 데이터베이스를 모으기 위해 데이터베이스와 내 ORM에서 모두 파싱하고 실행 취소하는 데 소비됨을 의미합니다.

ORM에 게시물의 댓글을 간절히로드하도록 지시하더라도 게시물에 대해 하나의 쿼리를 발송 한 다음 두 번째 쿼리를 사용하여 모든 댓글을 검색 한 다음 클라이언트 측에 정리하는 것이 가장 좋습니다. 또한 비효율적입니다.

관계형 데이터베이스는 입증 된 기술 (지옥보다 나이가 많음)이며 수십 년 동안 수많은 연구가 진행되어 왔으며, 그 이유가 실제로 존재한다고 확신합니다. SQL 표준)은 기능을 수행하도록 설계되었지만 위에서 설명한 접근 방식이 왜 불가능한지 잘 모르겠습니다. 레코드 간 가장 기본적인 관계 중 하나를 구현하는 가장 간단하고 명백한 방법 인 것 같습니다. 관계형 데이터베이스가 이와 같은 것을 제공하지 않는 이유는 무엇입니까?

(면책 조항 : 나는 대부분 Rails 및 NoSQL 데이터 저장소를 사용하여 웹 응용 프로그램을 작성하지만 최근 Postgres를 사용해 보았으며 실제로 그것을 좋아합니다. 관계형 데이터베이스를 공격한다는 의미는 아닙니다. 방금 혼란 스럽습니다.)

Rails 앱을 최적화하는 방법이나 특정 데이터베이스 에서이 문제를 해결하는 방법을 묻지 않습니다. 나는 SQL 표준이 반 직관적이고 낭비 적 인 것처럼 보일 때 왜 이런 식으로 작동하는지 묻고 있습니다. 원래의 SQL 디자이너가 결과를 이렇게 보이길 원하는 역사적인 이유가 있어야합니다.


1
모든 orms가 그렇게 작동하는 것은 아닙니다. 최대 절전 모드 / 최대 절전 모드에서는 조인을 지정할 수 있으며 단일 쿼리에서 전체 개체 트리를 열망 할 수 있습니다.
nathan nzalez

1
토론의 흥미로운 점은, 내가 아니에요 동안도 확인이는 ANSI의 SQL들과 회의를하지 않고 정말 답할 수있다
나단 곤잘레스

@nathan : 네, 전부는 아닙니다. 나는 주어진 쿼리 ( docs )에 대해 선호하는 접근 방식을 선택할 수있는 Sequel 을 사용 했지만 여전히 다중 쿼리 접근 방식을 권장합니다 (성능상의 이유로 생각합니다).

5
RDBMS는 세트를 저장하고 검색하도록 설계되었으므로 표시 할 데이터를 리턴하지 않습니다. 그것을 MVC처럼 생각하십시오-왜 모델을 느리게 사용하기 어렵거나 더 어렵게 만드는 비용으로 뷰를 구현하려고 시도합니까? RDBMS는 NoSQL 데이터베이스가 할 수없는 이점을 제공합니다 (또는 그 반대도 마찬가지 임). 문제를 해결하는 데 적합한 도구이기 때문에 데이터베이스를 사용하는 경우 데이터를 표시 할 준비가되어있는 데이터베이스를 반환하도록 요구하지 않습니다.

1
그들은 xml을
Ian

답변:


42

CJ Date는 7 장과 SQL 및 관계 이론 의 부록 B에서 이에 대해 자세히 설명 합니다. 맞습니다. 관계형 이론 에는 모든 행 에서 동일한 관계 유형 인 한 속성의 데이터 유형이 관계 자체가되는 것을 금지하는 것은 없습니다 . 당신의 예는 자격이 될 것입니다.

그러나 Date는 이와 같은 구조는 관계 계층 구조가 비대칭 적이므로 "보통은 아니지만 금기 사항"(즉, 나쁜 생각)이라고 말합니다 . 예를 들어 중첩 구조에서 익숙한 "평면"구조로의 변환을 중첩 시켜서 중첩을 다시 만들 수는 없습니다.

RVA (관계형 속성)를 허용하는 경우 RDBMS가 쿼리, 제약 조건 및 업데이트를보다 복잡하고 작성하기가 더 어렵고 지원하기가 더 어렵습니다.

최상의 관계 계층 구조가 명확하지 않기 때문에 데이터베이스 설계 원칙을 혼동하기도합니다 . 특정 공급 업체가 공급 한 부품에 대해 공급 업체와 중첩 된 RVA의 관계를 설계해야합니까? 또는 특정 부품을 공급하는 공급 업체의 부품과 중첩 된 RVA의 관계? 아니면 다른 유형의 쿼리를 쉽게 실행할 수 있도록 둘 다 저장합니까?

계층 적 데이터베이스문서 지향 데이터베이스 모델 에서 발생하는 것과 동일한 딜레마입니다 . 결국, 중첩 된 데이터 구조에 액세스하는 복잡성과 비용으로 인해 디자이너는 다른 쿼리로 쉽게 조회 할 수 있도록 데이터를 중복 저장해야합니다. 관계형 모델은 중복성을 권장하지 않으므로 RVA는 관계형 모델링의 목표에 맞설 수 있습니다.

내가 이해 한 것 (사용하지 않은 것)에서 RelDataphor 는 관계형 특성을 지원하는 RDBMS 프로젝트입니다.


@dportas의 의견 :

구조화 된 유형 은 SQL-99의 일부이며 Oracle은이를 지원합니다. 그러나 기본 테이블의 행당 중첩 테이블에 여러 튜플을 저장하지 않습니다. 일반적인 예는 기본 주소의 단일 열인 것처럼 보이지만 거리, 도시, 우편 번호 등에 대한 추가 하위 열이있는 "주소"속성입니다.

중첩 테이블 도 Oracle에서 지원하며 기본 테이블의 행당 여러 튜플을 허용합니다. 그러나 이것이 표준 SQL의 일부라는 것을 알지 못합니다. 한 블로그의 결론 을명심하십시오. "CREATE TABLE 문에 중첩 테이블을 사용하지 않습니다. 다시 유용하게 사용하기 위해 시간을 낭비하지 마십시오!"


3
한 관계를 다른 관계에 실제로 저장하고 싶지는 않습니다. 별도의 테이블에 있고 평소와 같이 비정규 화되었습니다. 조인 모델보다 나에게 더 직관적 인 것처럼 보일 때 왜 이런 종류의 결과 임베딩이 쿼리에 허용되지 않는지 묻습니다.
PreciousBodilyFluids

결과 집합과 테이블은 종류가 다릅니다. Date는 관계relvar를 각각 호출합니다 (비 유적으로 42는 정수인 반면 변수 x는 정수 42의 값을 가질 수 있음). 관계와 관계에 동일한 작업이 적용되므로 구조가 호환되어야합니다.
Bill Karwin

2
표준 SQL은 중첩 테이블을 지원합니다. 이를 "구조화 된 유형"이라고합니다. Oracle은이 기능이있는 하나의 DBMS입니다.
nvogel

2
데이터 중복을 피하기 위해 쿼리를 평평하고 데이터 복제 방식으로 작성해야한다고 주장하는 것이 다소 부끄럽지 않습니까?
Eamon Nerbonne

1
@EamonNerbonne, 관계 연산의 대칭. 예를 들어 투영. RVA에서 일부 하위 속성을 선택하는 경우 원래 계층 구조를 재현하기 위해 결과 집합에 대해 역 연산을 적용하려면 어떻게해야합니까? Date의 책 293 페이지가 Google 도서에있는 것을 발견했습니다. 그래서 그가 쓴 것을 볼 수 있습니다 : books.google.com/…
Bill Karwin

15

초기 데이터베이스 시스템 중 일부는 계층 데이터베이스 모델을 기반으로했습니다 . 이것은 부모님과 자녀가있는 구조와 같은 트리의 데이터를 나타냅니다. 여기서 제안하는 것처럼. HDMS는 관계형 모델을 기반으로 구축 된 데이터베이스로 대체되었습니다. 이것의 주된 이유는 RDBMS가 계층 적 데이터베이스에서는 어려운 "다 대다"관계를 모델링 할 수 있고 RDBMS가 원래 디자인의 일부가 아닌 쿼리를 쉽게 수행 할 수있는 반면 HDBMS는 디자인 타임에 지정된 경로를 통해 쿼리하도록 제한했기 때문입니다.

계층 구조 데이터베이스 시스템, 특히 Windows 레지스트리 및 LDAP의 일부 예가 여전히 있습니다.

이 주제에 대한 광범위한 내용은 다음 기사 에서 확인할 수 있습니다.


10

귀하의 질문은 데이터베이스가 견고한 논리를 기반으로하고 이론적 기초를 설정하는 동안 참조 무결성, 동시성을 보장하면서 2 차원 세트로 데이터를 저장, 조작 및 검색하는 데 매우 효과적이라는 사실에 중점을두고 있다고 가정합니다. 다른 많은 것들은 객체 지향 형식 또는 계층 적 형식이라고 부를 수있는 데이터를주고받는 (추가) 기능을 제공하지 않습니다.

그런 다음 "내 ORM에 게시물 댓글을 간절히로드하도록 지시하더라도 게시물에 대해 하나의 쿼리를 발송 한 다음 두 번째 쿼리를 사용하여 모든 댓글을 검색 한 다음 함께 정리하는 것이 가장 좋습니다 또한 클라이언트 측, 비효율적 " .

다음과 같이 두 개의 쿼리를 보내고 두 개의 배치 결과를 수신하는 데 비효율적 인 것은 없습니다.

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


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

(거의) 가장 효율적인 방법이라고 주장합니다 (거의 posts.id모든 열 이 필요하지는 않기 때문에 comments.*)

Todd가 그의 의견에서 지적한 것처럼, 데이터베이스에 데이터를 표시 할 준비가되도록 요청해서는 안됩니다. 그렇게하는 것은 응용 프로그램의 일입니다. 모든 디스플레이 작업에 필요한 결과를 얻기 위해 하나 또는 몇 개의 쿼리를 작성할 수 있으므로 db에서 응용 프로그램으로 와이어 (또는 메모리 버스)를 통해 전송되는 데이터에 불필요한 복제가 없습니다.

나는 ORM에 대해 말할 수는 없지만 그들 중 일부는 우리를 위해이 일의 일부를 할 수 있습니다.

웹 서버와 클라이언트간에 데이터를 전달할 때 비슷한 기술을 사용할 수 있습니다. 캐싱과 같은 다른 기술이 사용되므로 데이터베이스 (또는 웹 또는 다른 서버)에 중복 요청이 오버로드되지 않습니다.

내 생각에 SQL과 같은 표준은 한 영역에서 전문성을 유지하고 필드의 모든 영역을 다루지 않으면 가장 좋습니다.

반면에 SQL 표준을 설정 한 커미트는 미래에 다른 방식으로 생각하고 그러한 추가 기능에 대한 표준화를 제공 할 수 있습니다. 그러나 어느 날 밤에 디자인 할 수있는 것은 아닙니다.


1
내 응용 프로그램이 하나가 아닌 두 개의 데이터베이스 호출의 오버 헤드와 지연을 초래해야한다는 점에서 비효율적이었습니다. 그 외에도, 조인을 수행해도 표시 준비가 된 형식으로 데이터를 반환합니까? 아니면 데이터베이스 뷰를 사용하십니까? 원한다면 더 작은 쿼리를 실행하고 앱에서 함께 연결하여 쿼리를 피할 수도 있지만 여전히 유용한 도구입니다. 사용하기 쉽고 성능이 뛰어나다는 점 외에는 내가 제안하는 내용이 조인과 크게 다르다고 생각하지 않습니다.

2
@Precious : 여러 쿼리를 실행하기 위해 오버 헤드가 증가하지 않아도됩니다. 대부분의 데이터베이스를 사용하면 단일 쿼리에서 여러 쿼리를 제출하고 단일 쿼리에서 여러 결과 집합을받을 수 있습니다.
Daniel Pryden

@PreciousBodilyFluids-ypercube의 답변에있는 SQL 스 니펫은 단일 데이터베이스 호출로 전송되고 단일 응답으로 두 개의 결과 세트를 리턴하는 단일 쿼리입니다.
Carson63000

5

나는 적절하고 논쟁의 여지가있는 답변으로 대답 할 수 없으므로 내가 틀렸다면 나를 망각으로 내버려 두십시오 (그러나 새로운 것을 배울 수 있도록 수정하십시오). 그 이유는 관계형 데이터베이스가 관계형 모델을 중심으로하기 때문이라고 생각합니다. 관계형 모델은 "일차 논리"라는 것에 대해 전혀 알지 못하는 것을 기반으로합니다. 관계형 데이터베이스가 구축 된 수학적 / 논리적 틀에 개념적으로 맞지 않을 수도 있습니다. 또한 요청한 내용은 일반적으로 그래프 데이터베이스로 쉽게 해결되므로 달성하려는 것과 충돌하는 데이터베이스의 기본 개념이라는 힌트를 얻을 수 있습니다.


5

FOR XML을 사용할 때 적어도 SQLServer가 중첩 쿼리를 지원한다는 것을 알고 있습니다.

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')

여기서 문제는 RDBMS의 지원 부족이 아니라 테이블의 중첩 테이블 지원이 부족하다는 것입니다.

또한 내부 조인을 사용하지 못하게하는 요인은 무엇입니까?

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

내부 조인을 중첩 테이블로 실제로 볼 수 있으며 처음 두 필드의 내용 만 가능한 시간에 반복됩니다. 나는 조인의 성능에 대해 크게 걱정하지 않을 것입니다. 이와 같은 쿼리에서 유일한 느린 부분은 데이터베이스에서 클라이언트로의 io입니다. 콘텐츠에 많은 양의 데이터가 포함 된 경우에만 문제가됩니다. 이 경우 두 개의 쿼리를 제안합니다. 하나 select id, content는 내부 조인이 있고 하나는 내부 조인이 select posts.id, comments.*있습니다. 여전히 두 개의 쿼리 만 사용하므로 여러 게시물로 확장됩니다.


질문은 이것을 해결합니다. 두 번의 왕복을 수행해야하거나 (최적이 아님) 처음 두 열에 중복 데이터를 반환해야합니다 (최적의 것도 아님). 그는 최적의 솔루션을 원합니다 (제 의견으로는 비현실적이지 않음).
Scott Whitlock

나는 알고 있지만 최적의 솔루션으로 짜증나는 것은 없습니다. 내가 주장 할 수있는 유일한 것은 오버 헤드가 최소화되는 곳과 의존하는 곳입니다. 최적의 솔루션을 원한다면 벤치마킹하고 다른 접근법을 시도하십시오. 특정 상황에 따라 XML 솔루션조차 느려질 수 있으며 NoSQL 데이터 저장소에 익숙하지 않으므로와 비슷한 것이 있는지 말할 수 없습니다 for xml.
Dorus

5

실제로 Oracle은 원하는 것을 지원하지만 "cursor"키워드로 하위 쿼리를 래핑해야합니다. 열린 커서를 통해 결과를 가져옵니다. 예를 들어 Java에서는 주석이 결과 집합으로 표시됩니다. 이에 대한 자세한 내용은 "CURSOR Expression" 에 대한 Oracle 설명서를 참조하십시오.

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

1

일부는 중첩 (계층 적)을 지원합니다.

하나의 쿼리를 원한다면 자체 참조하는 하나의 테이블을 가질 수 있습니다. 일부 RDMS는이 개념을 지원합니다. 예를 들어, SQL Server를 사용하면 계층 적 쿼리에 CTE (Common Table Expressions)를 사용할 수 있습니다.

귀하의 경우 게시물은 레벨 0에 있고 모든 의견은 레벨 1에 있습니다.

다른 옵션은 2 개의 쿼리 또는 반환 된 모든 레코드 (다른 사람들이 언급 한)에 대한 추가 정보가있는 조인입니다.

계층의 예 :

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

위의 링크에서 EmpLevel은 중첩 수준 (또는 계층)을 보여줍니다.


SQL Server에서 하위 결과에 대한 설명서를 찾을 수 없습니다. CTE를 사용하는 경우에도 마찬가지입니다. 결과적으로 필자는 충분히 강력한 형식의 열이있는 데이터 행을 의미합니다. 답변에 참조를 추가 할 수 있습니까?
SandRock

@SandRock-데이터베이스는 SQL 쿼리에서 단일 결과 집합을 다시 보냅니다. 쿼리 자체에서 수준을 식별하면 처리해야 할 계층 적 또는 중첩 된 결과 집합을 만들 수 있습니다. 현재는 가장 가까운 것으로 생각되며 중첩 된 데이터를 반환하는 데 가장 가깝습니다.
Jon Raynor

0

죄송하지만 귀하의 문제를 정확하게 이해하지 못했습니다.

MSSQL에서는 2 개의 SQL 문만 실행할 수 있습니다.

SELECT id, content
FROM posts
WHERE id = 7

SELECT * FROM comments WHERE post_id = 7

그리고 2 개의 결과 집합을 동시에 반환합니다.


질문을하는 사람은 데이터베이스에 두 번의 왕복이 발생하기 때문에 효율성이 떨어지며 일반적으로 오버 헤드로 인해 왕복을 최소화하려고한다고 말합니다. 그는 한 번의 왕복 여행을 원하고 두 테이블을 모두 되 찾으려고합니다.
Scott Whitlock

그러나 한 번의 왕복 여행이 될 것입니다. stackoverflow.com/questions/2336362/…
Biff MaGriff

0

RDBM은 이론을 기반으로하며 이론에 충실합니다. 이것은 약간의 일관성과 수학적으로 입증 된 신뢰성을 허용합니다.

모델은 단순하고 이론을 기반으로하기 때문에 사람들이 최적화와 많은 구현을 쉽게 할 수 있습니다. 이것은 모두가 약간 다른 NoSQL과 다릅니다.

과거에는 계층 적 데이터베이스를 만들려고 시도했지만 IIRC (Google에서는 볼 수 없음)에 문제 (사이클과 평등이 떠오름)가있었습니다.


0

특정한 필요가 있습니다. 원하는 형식으로 데이터베이스에서 데이터를 추출하는 것이 바람직하므로 원하는 작업을 수행 할 수 있습니다.

데이터베이스가 수행하지 않는 것들도 있지만, 어쨌든 데이터베이스를 구축하는 것은 불가능하지 않습니다. 다른 응용 프로그램으로 서식을 유지하는 것이 현재 권장 사항이지만 그 이유를 설명 할 수는 없습니다.

내가 당신의 제안에 반대하는 유일한 주장은이 결과 세트를 "sql"방식으로 처리 할 수 ​​있다는 것입니다. 데이터베이스에서 결과를 작성하거나 데이터베이스에서 작업하거나 조작 할 수없는 것은 나쁜 생각입니다. 제안한 방식으로 빌드 된 뷰를 작성했다고 가정 해 봅시다. 다른 select 문에 어떻게 포함합니까? 데이터베이스는 결과를 가져 와서 일하는 것을 좋아합니다. 다른 테이블에 어떻게 결합합니까? 결과 집합을 다른 집합과 어떻게 비교합니까?

그런 다음 RDMS의 장점은 SQL의 유연성입니다. 테이블에서 데이터를 선택하는 구문은 시스템의 사용자 또는 다른 개체 목록과 매우 비슷합니다 (적어도 목표입니다). 완전히 다른 것을 수행해야 할 점이 있는지 확실하지 않습니다. 그들은 절차 적 코드 / 커서 또는 데이터의 BLOBS를 매우 효율적으로 처리하는 시점까지 얻지 못했습니다.


0

제 생각에는 주로 SQL과 집계 쿼리가 수행되는 방식 때문입니다. 집계 함수와 그룹화는 큰 2 차원 행 집합에서 실행되어 결과를 반환합니다. 이것이 처음부터 그랬고 매우 빠릅니다 (대부분의 NoSQL 솔루션은 집계 속도가 느리고 복잡한 쿼리 대신 비정규 화 된 스키마에 의존합니다)

물론 PostgreSQL에는 객체 지향 데이터베이스의 일부 기능이 있습니다. 이 메일 ( message ) 에 따르면 사용자 정의 집계를 작성하여 필요한 것을 얻을 수 있습니다.

개인적으로 집계 응용 프로그램을 수행하고 지연 로딩과 같은 기능을 지원하여 성능을 향상시키는 Doctrine ORM (PHP)과 같은 프레임 워크를 사용하고 있습니다.


0

PostgreSQL은 ArraysJSON 등 다양한 구조화 된 데이터 유형을 지원합니다 . SQL 또는 내장 프로 시저 언어 중 하나를 사용하여 임의로 복잡한 구조로 값을 작성하고이를 애플리케이션으로 리턴 할 수 있습니다. 구조화 된 유형의 열을 사용하여 테이블을 만들 수도 있지만 디자인을 불완전하게 비정규 화하는지 여부를 신중하게 고려해야합니다.


1
이것은 이전의 13 가지 답변에서 제시되고 설명 된 점들에 비해 실질적인 것을 제공하지 않는 것 같습니다
gnat

이 질문에는 특히 JSON이 언급되어 있으며이 답변은 JSON이 하나 이상의 RDBMS에서 쿼리로 반환 될 수 있음을 지적하는 유일한 방법입니다. 오히려 그 질문이 잘못된 전제에 기초하고 있으므로 어떤 명확한 대답도 기대할 수 없다고 말한 것입니다. 그러나 StackExchange는 그렇게 할 수 없습니다.
Jonathan Rogers
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.