Perché i database relazionali non supportano la restituzione delle informazioni in un formato nidificato?

46

Supponiamo che sto costruendo un blog che voglio avere post e commenti. Creo quindi due tabelle, una tabella "post" con una colonna "id" intero con incremento automatico e una tabella "commenti" con una chiave esterna "post_id".

Quindi voglio eseguire quella che probabilmente sarà la mia query più comune, ovvero recuperare un post e tutti i suoi commenti. Essendo piuttosto nuovo nei database relazionali, l'approccio che mi sembra più ovvio è scrivere una query che assomiglierebbe a qualcosa:

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

Quale mi darebbe l'id e il contenuto del post che desidero, insieme a tutte le righe di commento pertinenti impacchettate ordinatamente in un array (una rappresentazione annidata come si userebbe in JSON). Naturalmente, i database SQL e relazionali non funzionano in questo modo, e il più vicino possibile è quello di fare un join tra "post" e "commenti" che restituirà un sacco di inutili duplicazioni di dati (con le stesse informazioni postate ripetute in ogni riga), il che significa che il tempo di elaborazione viene speso sia sul database per mettere tutto insieme che sul mio ORM per analizzare e annullare tutto.

Anche se istruisco il mio ORM a caricare avidamente i commenti del post, il meglio che faremo è di inviare una query per il post, e poi una seconda query per recuperare tutti i commenti, e poi metterli insieme client- lato, che è anche inefficiente.

Capisco che i database relazionali sono una tecnologia collaudata (diavolo, sono più vecchi di me) e che c'è stata una grande quantità di ricerche nel corso dei decenni, e sono sicuro che ci sia davvero una buona ragione per cui (e lo standard SQL) sono progettati per funzionare come fanno, ma non sono sicuro del motivo per cui l'approccio che ho delineato sopra non è possibile. Mi sembra il modo più semplice e ovvio per implementare una delle relazioni più basilari tra i record. Perché i database relazionali non offrono qualcosa di simile?

(Disclaimer: per lo più scrivo webapp usando i datastore di Rails e NoSQL, ma recentemente ho provato Postgres e mi piace molto, non intendo attaccare i database relazionali, sono solo perplesso. )

Non sto chiedendo come ottimizzare un'app Rails o come risolvere il problema in un determinato database. Sto chiedendo perché lo standard SQL funziona in questo modo quando sembra controintuitivo e dispendioso per me. Ci deve essere una ragione storica per cui i designer originali di SQL volevano che i loro risultati assomigliassero a questo.

    
posta PreciousBodilyFluids 06.07.2011 - 23:05
fonte

12 risposte

42

C. J. Date entra nei dettagli su questo nel Capitolo 7 e Appendice B di Teoria SQL e relazionale . Hai ragione, non c'è nulla nella teoria relazionale che proibisca al tipo di dati di un attributo di essere una relazione stessa, purché sia lo stesso tipo di relazione su ogni riga. Il tuo esempio si qualifica.

Ma Date dice che strutture come questa sono "di solito - ma non invariabilmente - controindicate" (cioè una cattiva idea) perché le gerarchie di relazioni sono asimmetriche . Ad esempio, una trasformazione dalla struttura nidificata a una struttura "piatta" familiare non può sempre essere annullata per ricreare il nidificazione.

Le query, i vincoli e gli aggiornamenti sono più complessi, più difficili da scrivere e più difficile da supportare per RDBMS se si abilitano gli attributi a valori relazionali (RVA).

Inoltre confonde i principi di progettazione del database, perché la gerarchia di relazioni migliore non è così chiara. Dovremmo progettare una relazione dei fornitori con un RVA annidato per le parti fornite da un determinato fornitore? O una relazione di parti con un RVA annidato per i fornitori che forniscono una determinata parte? Oppure memorizza entrambi, per semplificare l'esecuzione di diversi tipi di query?

Questo è lo stesso dilemma derivante dal database gerarchico e database orientato ai documenti . Alla fine, la complessità e il costo dell'accesso a strutture di dati nidificate consentono ai progettisti di archiviare i dati in modo ridondante per una ricerca più semplice da parte di query diverse. Il modello relazionale scoraggia la ridondanza, quindi gli RVA possono lavorare contro gli obiettivi della modellazione relazionale.

Da quello che ho capito (non li ho usati), Rel e Dataphor sono progetti RDBMS che supportano attributi con valori relazionali.

Re commento da @dportas:

I tipi strutturati fanno parte di SQL-99 e Oracle li supporta. Ma non memorizzano più tuple nella tabella annidata per riga della tabella di base. L'esempio comune è un attributo "indirizzo" che sembra essere una singola colonna della tabella di base, ma ha ulteriori sotto-colonne per strada, città, codice postale, ecc.

Tabelle nidificate sono supportati anche da Oracle e consentono più tuple per riga della tabella di base. Ma non sono consapevole che questo fa parte dello standard SQL. Tieni presente la conclusione di un blog: "Non userò mai una tabella nidificata in un'istruzione CREATE TABLE. Trascorri tutto il tuo tempo UN-NESTING per renderli nuovamente utili!"

    
risposta data 07.07.2011 - 00:45
fonte
15

Alcuni dei primi sistemi di database erano basati sul modello di database gerarchico . Questo rappresentava i dati in una struttura ad albero con genitori e figli, proprio come si sta suggerendo qui. HDMS sono stati in gran parte sostituiti da database costruiti sul modello relazionale. I motivi principali per questo erano che RDBMS poteva modellare relazioni "da molti a molti" che erano difficili per i database gerarchici e che RDBMS poteva facilmente eseguire query che non facevano parte del progetto originale mentre HDBMS ti costringeva a interrogare attraverso percorsi specificati in fase di progettazione.

Ci sono ancora alcuni esempi di sistemi di database gerarchici in natura, in particolare il registro di Windows e LDAP.

L'ampia copertura di questo argomento è disponibile nel seguente articolo

    
risposta data 07.07.2011 - 00:05
fonte
10

Suppongo che la tua domanda sia davvero centrata sul fatto che mentre i database sono basati su una logica solida e impostano le basi terapeutiche e fanno un ottimo lavoro nell'archiviazione, manipolazione e recupero dei dati in insiemi (bidimensionali) garantendo al contempo referenziale integrità, concorrenza e molte altre cose, non forniscono una funzione (aggiuntiva) di invio (e ricezione) di dati in ciò che si potrebbe chiamare formato orientato agli oggetti o in formato gerarchico.

Quindi affermi che "anche se ordino al mio ORM di caricare con impazienza i commenti del post, il meglio che fare è inviare una query per il post e poi una seconda query per recuperare tutti i commenti e poi li metti insieme sul lato client, che è anche inefficiente ".

Non vedo nulla di inefficiente nell'invio di 2 query e nella ricezione di 2 lotti di risultati con:

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


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

Direi che è (quasi) il modo più efficace (quasi, dato che non hai davvero bisogno del posts.id e non di tutte le colonne da comments.* )

Come ha sottolineato Todd nel suo commento, non dovresti chiedere al database di restituire i dati pronti per la visualizzazione. È compito dell'applicazione farlo. È possibile scrivere (una o poche) query per ottenere i risultati necessari per ogni operazione di visualizzazione in modo che non vi sia alcuna duplicazione non necessaria nei dati inviati tramite il filo (o il bus di memoria) dal db all'applicazione.

Non posso parlare di ORM in realtà, ma forse alcuni di loro possono fare parte di questo lavoro per noi.

Tecniche simili possono essere utilizzate nella consegna di dati tra un server web e un client. Altre tecniche (come la memorizzazione nella cache) vengono utilizzate in modo che il database (o il web o altro server) non sia sovraccaricato di richieste duplicate.

La mia ipotesi è che gli standard, come SQL, siano i migliori se rimangono specializzati in un'area e non cercano di coprire tutte le aree di un campo.

D'altra parte, il comitato che definisce lo standard SQL potrebbe pensare diversamente in futuro e fornire la standardizzazione per tale caratteristica aggiuntiva. Ma non è qualcosa che può essere progettato in una sola notte.

    
risposta data 07.07.2011 - 00:53
fonte
5

Non sono in grado di rispondere con una risposta corretta e argomentata, quindi sentitevi liberi di mandarmi in oblio se mi sbaglio (ma per favore correggetemi in modo che possiamo imparare qualcosa di nuovo). Penso che la ragione sia che i database relazionali sono centrati sul modello relazionale, che a sua volta si basa su qualcosa di cui non so nulla chiamato "logica del primo ordine". Quello che potreste chiedere probabilmente non è concettualmente inserito nel database matematico / logico su cui sono costruiti i database relazionali. Inoltre, ciò che chiedi è generalmente risolto facilmente dai database di grafici, dando più suggerimenti sul fatto che si tratta della concettualizzazione sottostante del database che è in conflitto con ciò che vuoi ottenere.

    
risposta data 06.07.2011 - 23:25
fonte
5

So che SQLServer supporta le query nidificate quando si utilizza 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')

Il problema qui non è la mancanza di supporto da parte di RDBMS, ma mancanza di supporto delle tabelle nidificate nelle tabelle.

Inoltre, cosa ti impedisce di usare un inner join?

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

È possibile guardare effettivamente il join interno come una tabella nidificata, solo il contenuto dei primi 2 campi viene ripetuto per un tempo possibile. Non mi preoccuperei molto delle prestazioni del join, l'unica parte lenta in una query come questa è l'io dal database al client. Questo sarà solo un problema quando il contenuto contiene una grande quantità di dati. In tal caso suggerirei due query, una con select id, content e una con un inner join e select posts.id, comments.* . Ciò si ridimensiona anche con più post, in quanto si utilizzano ancora solo 2 query.

    
risposta data 07.07.2011 - 11:23
fonte
5

In realtà, Oracle supporta ciò che vuoi ma devi avvolgere la sotto-query con la parola chiave "cursore". I risultati sono recuperati tramite il cursore aperto. In Java, ad esempio, i commenti verrebbero visualizzati come set di risultati. Ulteriori informazioni su questo argomento sono la documentazione di Oracle su "Espressione CURSOR"

SELECT id, content, cursor(SELECT * FROM comments WHERE post_id = 7) AS comments
FROM posts
WHERE id = 7
    
risposta data 10.07.2011 - 01:40
fonte
1

Alcuni supportano il nesting (gerarchico).

Se volevi una query, potresti avere una tabella che si autorappresenta. Alcuni RDMS supportano questo concetto. Ad esempio, con SQL Server è possibile utilizzare Common Table Expressions (CTE) per una query gerarchica.

Nel tuo caso i Post sarebbero al Livello 0 e quindi tutti i commenti sarebbero al Livello 1.

Le altre opzioni sono 2 query o un join con alcune informazioni aggiuntive per ogni record restituito (che altri hanno menzionato).

Esempio di gerarchico:

link

Nel link sopra, EmpLevel mostra il livello del nidificazione (o della gerarchia).

    
risposta data 07.07.2011 - 02:18
fonte
0

Mi dispiace non sono sicuro di aver compreso esattamente il tuo problema.

In MSSQL puoi semplicemente eseguire 2 istruzioni SQL.

SELECT id, content
FROM posts
WHERE id = 7

SELECT * FROM comments WHERE post_id = 7

E restituirà i tuoi 2 set di risultati contemporaneamente.

    
risposta data 06.07.2011 - 23:33
fonte
0

Gli RDBM sono basati sulla teoria e si attengono alla teoria. Ciò consente una buona coerenza e un'affidabilità provata matematicamente.

Poiché il modello è semplice e di nuovo basato sulla teoria, rende facile per le persone ottimizzare e molte implementazioni. Questo è diverso da NoSQL dove tutti lo fanno leggermente diverso.

Ci sono stati tentativi in passato di creare database gerarchici ma IIRC (non riesco a google) ci sono stati problemi (i cicli e l'uguaglianza vengono in mente).

    
risposta data 06.07.2011 - 23:54
fonte
0

Hai un'esigenza specifica. Sarebbe preferibile estrarre i dati da un database nel formato desiderato, in modo da poterlo fare con ciò che si desidera.

Alcuni database di cose non funzionano altrettanto bene, ma non è impossibile costruirli per farlo comunque. Lasciare la formulazione ad altre applicazioni è la raccomandazione corrente, ma non giustifica il motivo per cui non può essere fatto.

L'unica argomentazione contro il tuo suggerimento è riuscire a gestire questo set di risultati in modo "sql". Sarebbe una cattiva idea creare un risultato nel database e non essere in grado di lavorare con esso o manipolarlo in una certa misura. Diciamo che ho creato una vista costruita come suggerisci, come inserirla in un'altra istruzione select? Ai database piace prendere risultati e fare cose con loro. Come potrei unirmi ad un altro tavolo? Come potrei confrontare il tuo set di risultati con un altro?

Quindi il vantaggio di RDMS è la flessibilità di sql. La sintassi per selezionare i dati da una tabella è molto simile a un elenco di utenti o altri oggetti nel sistema (o almeno questo è l'obiettivo.). Non sono sicuro che non vi sia alcun motivo per fare qualcosa di completamente diverso. Non li hanno nemmeno portati al punto di gestire codice procedurale / cursori o BLOBS di dati in modo molto efficiente.

    
risposta data 26.09.2011 - 19:16
fonte
0

Secondo me è principalmente a causa di SQL e del modo in cui vengono eseguite le query aggregate: le funzioni di aggregazione e il raggruppamento vengono eseguiti su set di righe bidimensionali di grandi dimensioni per restituire risultati. Questo è il modo in cui è stato dall'inizio ed è molto veloce (la maggior parte delle soluzioni NoSQL è piuttosto lenta con l'aggregazione e si basa su schemi denormalizzati invece di query complesse)

Naturalmente PostgreSQL ha alcune caratteristiche del database orientato agli oggetti. Secondo questo messaggio ( messaggio ) puoi ottenere ciò che ti serve creando un aggregato personalizzato.

Personalmente sto usando framework come Doctrine ORM (PHP) che fanno l'aggregazione lato applicazione e supportano funzionalità come lazy-loading per aumentare le prestazioni.

    
risposta data 06.07.2011 - 23:46
fonte
0

PostgreSQL supporta una varietà di tipi di dati strutturati, tra cui Array e JSON . Utilizzando SQL o una delle lingue procedurali incorporate, è possibile creare valori con una struttura arbitrariamente complessa e restituirli alla propria applicazione. Puoi anche creare tabelle con colonne di qualsiasi tipo strutturato, anche se dovresti considerare attentamente se stai denormalizzando inutilmente il tuo design.

    
risposta data 13.09.2016 - 09:31
fonte

Leggi altre domande sui tag