DynamoDB JOIN: come unire le tabelle
Non c'è "JOIN" in DynamoDB. Il API non ha un operatore di join, il modello dati
non ha chiavi esterne e, la parte che sorprende la maggior parte delle persone, , il
Il livello di query in stile SQL non ne aggiunge nemmeno uno. Si legge A PartiQL SELECT
esattamente una tabella.
Se provieni da un database relazionale, questo è il primo muro che colpisci. Questo la guida spiega perché il muro è lì, le quattro cose che fanno invece gli sviluppatori, il un caso in cui hai davvero bisogno di un vero join e come eseguirne uno.
DynamoDB può eseguire unioni?
No. DynamoDB non può unirsi ai tabelle, non tramite il API di basso livello (GetItem /
Query / Scan / BatchGetItem), non passante, e non attraverso nessuno
pianificatore di query integrato, perché non ce n'è. Ogni lettura viene mappata su una tabella o
uno dei suoi indici; combinare due tabelle su una chiave corrispondente è qualcosa che fai
nella tua app dopo DynamoDB restituisce gli elementi, mai al suo interno.
- DynamoDB non ha nessun operatore
JOIN. Non lo è mai stato. SELECTdi PartiQL è solo a tabella singola — la grammatica è letteralmenteSELECT … FROM {{table}}[.{{index}}]e puntandolo su due tabelle restituisceValidationException: è supportata solo la selezione da una singola tabella o indice.- La soluzione consigliata da AWS è quella di non aver bisogno di un'unione:o utilizzare design a tabella singola in modo che gli elementi correlati risiedano in una partizione recuperata in un'unica richiesta.
- Per il vero caso cross-tabella/ad hoc, ti iscrivi fuori DynamoDB — in la tua app o con uno strumento che lo fa per te.
Perché DynamoDB non ha join
Un SQL JOIN chiede al database di leggere più tabelle e assemblarle durante la query
tempo. AWS proprio
guida alla modellazione dei dati relazionali
spiega il costo: una query del tipo
SELECT * FROM Orders
INNER JOIN Order_Items ON Orders.Order_ID = Order_Items.Order_ID
INNER JOIN Products ON Products.Product_ID = Order_Items.Product_ID
INNER JOIN Inventories ON Products.Product_ID = Inventories.Product_ID
ORDER BY Quantity_on_Hand DESCè flessibile, ma "ogni join nella query aumenta la complessità di runtime del query perché i dati di ogni tabella devono essere organizzati e quindi assemblati." Quello il lavoro è illimitato: il tuo costo dipende dai dati, non dalla query, il che è esattamente la proprietà DynamoDB rifiuta di avere.
Quindi AWS ha progettato il vincolo. DynamoDB è, nelle loro parole, "costruito per
ridurre al minimo i vincoli [della CPU e della rete] eliminando i JOIN (e
incoraggiando la denormalizzazione dei dati) e ottimizzando l'architettura del database
rispondere completamente a una query dell'applicazione con una singola richiesta a un articolo." Quelli lo sono
le qualità che acquistano una latenza di un millisecondo a una cifra su qualsiasi scala: il
il costo di runtime di una lettura DynamoDB è costante indipendentemente dalle dimensioni della tabella. C'è
nessun motore di join e nessun concetto di chiave esterna contro cui pianificare, in base alla progettazione.
"Ma PartiQL è SQL, sicuramente si unisce?"
No. PartiQL ti fornisce la sintassi SELECT / INSERT / UPDATE / DELETE
DynamoDB, ma è SQL-compatibile, non SQL. Il
grammatica ufficiale SELECT
è:
SELECT {{expression}} [, ...]
FROM {{table}}[.{{index}}]
[ WHERE {{condition}} ]
[ ORDER BY {{key}} [DESC|ASC], ... ]FROM accetta una tabella (facoltativamente uno dei suoi indici). Non esiste un secondo
Tabella "FROM", niente "JOIN", niente sottoquery, niente CTE. Li abbiamo giocati tutti e tre contro DynamoDB
per vedere esattamente come ognuno fallisce.
Un "JOIN" esplicito:
SELECT o.pk FROM "Orders" o JOIN "Customers" c ON o.customerId = c.pkValidationException: Only select from a single table or index is supported.Due tabelle in FROM: stesso rifiuto, quindi il motore non rifiuta JOIN
parola chiave, rifiuta la seconda tabella:
SELECT * FROM "Orders", "Customers"ValidationException: Only select from a single table or index is supported.Una sottoquery fallisce in modo diverso, il che vale la pena sapere se ne stai eseguendo il debug.
PartiQL non raggiunge affatto un controllo multi-tabella: rifiuta l'operando IN
prima, quindi riceverai un messaggio che non menziona mai le tabelle:
SELECT * FROM "Orders" WHERE customerId IN (SELECT pk FROM "Customers")ValidationException: IN operator must have a left hand argument of type Variable
Reference and right hand argument of type Seq with at least one memberNessuna frase ti fa entrare. Il primo due muoiono sulla seconda tabella e il terzo muore anche prima, sull'operando.
Se vuoi il ragionamento completo sul perché PartiQL assomiglia a SQL ma non puoi comportarti bene mi piace, vedi PartiQL vs SQL.
Le 4 soluzioni alternative attualmente utilizzate dagli sviluppatori
1. Denormalizza (copia i dati)
Memorizza i campi che altrimenti uniresti direttamente nell'elemento. Un "Ordine" porta un'istantanea di "customerName" e "shippingAddress" invece di un file "customerId" che risolveresti in seguito. Una lettura, nessuna partecipazione.
Il costo è il fan-out del tempo di scrittura. Quando la fonte cambia, aggiorni ogni copia (tipicamente tramite agestore). Stai scambiando la complessità di lettura per scrivere complessità: di solito un buon scambio per un'app ad alta lettura.
2. Progettazione a tabella singola (pre-unione nella partizione)
Inserisci le entità correlate in una tabella sotto una chiave di partizione condivisa in modo che
è il risultato unito. Un cliente e tutti i suoi ordini vengono condivisi
PK = "CLIENTE#42"; un Query restituisce l'articolo del cliente più ogni ordine
item: l'"unione" è già avvenuta al momento della scrittura.
Query PK = "CUSTOMER#42"
→ CUSTOMER#42 / PROFILE (the customer)
→ CUSTOMER#42 / ORDER#1001 (an order)
→ CUSTOMER#42 / ORDER#1002 (an order)
Questa è la risposta canonica DynamoDB alle relazioni uno-a-molti. Pieno procedura dettagliata in progettazione a tabella singola.
3. Unione lato applicazione (due letture, punto nel codice)
Leggi dalla tabella A, prendi le chiavi che hai recuperato, leggi dalla tabella B e unisci i file due set di risultati nell'applicazione. È la logica di unione relazionale, semplicemente in esecuzione nel tuo codice anziché nel database:
// "Get each order with its customer name" — the manual join.
const {Items: orders} = await ddb.query({TableName: 'Orders' /* … */});
const customers = await Promise.all(
orders.map((o) => ddb.get({TableName: 'Customers', Key: {id: o.customerId}}))
);
const joined = orders.map((o, i) => ({
...o,
customerName: customers[i].Item?.name
}));Va bene per un piccolo fan-out. Con molti ordini diventa un problema N+1: una lettura
per elencare gli ordini, quindi una lettura per ordine, il che è lento e brucia la capacità di lettura.
BatchGetItem (successivo) comprime la seconda ondata in un viaggio di andata e ritorno.
4. BatchGetItem (un viaggio di andata e ritorno, più tabelle)
BatchGetItem
è la cosa più vicina che API arriva a "toccare due tabelle contemporaneamente": uno
request restituisce "gli attributi di uno o più elementi da uno o più
tabelle", fino a 100 elementi o 16 MB per chiamata, a seconda di quale evento venga raggiunto per primo. Esso
taglia i viaggi di andata e ritorno di un join lato app, ma non è un join. Tu
"identificare gli elementi richiesti tramite chiave primaria"; non esiste alcuna condizione "ON" e no
corrispondenza relazionale. Devi ancora conoscere le chiavi in primo piano e cucirle
risposte insieme tu stesso.
Quando un vero JOIN è inevitabile
Le quattro soluzioni alternative coprono bene i percorsi di lettura della produzione. Dove cadono è la query ad hoc, esplorativa, analitica, quella per cui non hai modellato:
- "Quali clienti nell'UE hanno effettuato un ordine superiore a $ 500 il mese scorso?" attraverso an Tabella "Ordini" e tabella "Clienti".
- Un controllo una tantum della qualità dei dati che unisce due tipi di entità.
- Reporting e aggregati (
GROUP BY,SUM,COUNT) — che DynamoDB non ha operatore per niente.
Queste sono esattamente le query che non puoi pre-inserire in una partizione, perché by definizione che non sapevi di chiedere loro. L'istinto relazionale — scrivi a "ISCRIVITI" — è quello giusto qui. DynamoDB semplicemente non posso servirlo in modo nativo, e nemmeno PartiQL può.
La solita risposta dei pesi massimi è: esporta in S3 ed esegui query con Athena (o utilizzare il connettore di query federato di Athena per "JOIN" su una tabella live) o collegarsi a un magazzino. Questo è corretto per l'analisi reale su larga scala, ma è a un sacco di complicazioni per una domanda a cui vuoi una risposta adesso, rispetto al tuo tabella dal vivo.
Esecuzione di un vero JOIN con DynoTable di SQL Workbench
DynoTable è un client desktop DynamoDB il cui SQL Workbench viene eseguito
effettivo SQL — inclusi JOIN, GROUP BY e funzioni aggregate — sul tuo
Tabelle DynamoDB. Legge gli elementi attraverso il normale DynamoDB API, quindi
esegue le parti relazionali della query nel client. Quindi puoi scrivere:
SELECT c.name, SUM(o.total) AS spend
FROM Customers c
JOIN Orders o ON o.customerId = c.id
WHERE c.region = 'EU'
GROUP BY c.name
HAVING SUM(o.total) > 500- e ottieni un set di risultati, rispetto a tabelle che non hanno alcuna relazione definita e a motore di query che non ha la parola chiave "JOIN".
L'onesto avvertimento: "entro le regole del modello di accesso di DynamoDB": il Workbench
legge ancora DynamoDB, quindi un join illimitato è una lettura illimitata. Il
Le query più veloci sono quelle in cui la clausola "WHERE" (o la clausola "ON" del join
attributo) colpisce una chiave di partizione o un GSI su almeno un
lato, quindi DynamoDB esegue un Query anziché una tabella completa
scan prima dell'esecuzione del join. Il Workbench no
abroga i vincoli di questa guida: ti consente semplicemente di porre la domanda SQL
invece di scrivere tu stesso il punto a mano e ti dice cosa sta facendo
sotto.
Tra i client GUI, è l'unico "sì, puoi unirti" che è effettivamente vero: PartiQL e AWS NoSQL Workbench
- il cui generatore di operazioni esegue operazioni a tabella singola e istruzioni PartiQL (no JOIN, no SELECT multi-tabella) - entrambi si fermano al muro a tabella singolo, come fa la maggior parte altri client GUI. Guarda come DynoTable si confronta con a DynamoDB GUI.
FAQ
PartiQL supporta JOIN?
No. SELECT di PartiQL legge una singola tabella (o uno dei suoi indici). A
la query su più tabelle restituisce "ValidationException: seleziona solo da una singola tabella o
l'indice è supportato.` Stesso muro del resto del API.
Puoi unire due tabelle DynamoDB in una query?
Non nativamente. Il DynamoDB API non ha alcuna istruzione che legga due tabelle e corrisponda
loro su una chiave. BatchGetItem può leggere elementi da più tabelle in una richiesta,
ma non ha alcuna condizione "ON": restituisce gli elementi nominati in base alla chiave primaria e
lascia a te l'abbinamento. Un vero JOIN…ON… avviene solo all'esterno del DynamoDB:
nella tua app o nel SQL Workbench di DynoTable.
Puoi unirti a una tabella al suo GSI?
No, un Global Secondary Index non è una tabella separata
unirsi a; è una vista chiave alternativa degli stessi elementi. Tu Query o
la tabella o l'indice in un dato SELECT, non entrambi uniti insieme. A GSI
ti consente di raggiungere gli elementi con una chiave diversa, il che spesso elimina la necessità di a
aderire in primo luogo.
Puoi iscriverti a due account AWS (o due tabelle in account diversi)?
Non in modo nativo: non esiste una primitiva di unione tra account. BatchGetItem può raggiungere
la tabella di un altro account se la politica basata sulle risorse di quella tabella lo consente al chiamante
(da marzo 2024), ma non ha ancora la condizione "ON", quindi è un
lettura multitabella, non un join. Leggeresti ogni lato e uniresti i risultati nel tuo
applicazione o in uno strumento come DynoTable di Workbench.
La denormalizzazione è davvero migliore dell'unione? Per il carico di lavoro target di DynamoDB (letture prevedibili e ad alto volume) sì. Ti muovi il costo per scrivere tempo (e accettare parte della duplicazione dei dati) in cambio la richiesta singola legge quella scala in modo piatto. Il La guida alla progettazione di una tabella singola copre i compromessi.
Costruire manualmente le chiavi e le condizioni per queste letture è complicato: il
costruttore di espressioni genera il file
la sintassi KeyConditionExpression / FilterExpression per te e
DynoTable esegue il vero SQL quando una soluzione alternativa non lo risolve.
PartiQL messaggi di rifiuto riprodotti l'11-08-2026 contro DynamoDB Local (us-east-1,
@aws-sdk/client-dynamodb v3.1096.0), citato testualmente da ValidationException.message.