DynamoDB JOIN: Como ingressar em tabelas
Não há JOIN em DynamoDB. O API não possui operador de junção, o modelo de dados
não possui chaves estrangeiras e - a parte que surpreende a maioria das pessoas - , o
Camada de consulta com sabor SQL, também não adiciona nenhuma. Um PartiQL SELECT lê
exatamente uma mesa.
Se você veio de um banco de dados relacional, esta é a primeira barreira que você encontra. Isto guia cobre por que o muro está lá, as quatro coisas que os desenvolvedores fazem, o um caso em que você realmente precisa de uma junção real – e como executá-la.
DynamoDB pode fazer junções?
Não. DynamoDB não pode ingressar em tabelas - não através do API de baixo nível (GetItem /
Query / Scan / BatchGetItem), não através, e não através de qualquer
planejador de consultas integrado, porque não há nenhum. Cada leitura é mapeada para uma tabela ou
um de seus índices; combinar duas tabelas em uma chave correspondente é algo que você faz
no seu aplicativo depois DynamoDB retorna os itens, nunca dentro dele.
- DynamoDB não possui operador
JOIN. Nunca aconteceu. - O
SELECTde PartiQL é apenas tabela única — a gramática é literalmenteSELECT… FROM {{table}}[.{{index}}], e apontar para duas tabelas retornaValidationException: apenas a seleção de uma única tabela ou índice é suportada. - A correção que AWS recomenda é não precisar de uma junção:, ou usar design de tabela única para que os itens relacionados fiquem uma partição que você busca em uma única solicitação.
- Para o caso genuíno de mesa cruzada/ad-hoc, você participa fora DynamoDB — em seu aplicativo ou com uma ferramenta que faça isso por você.
Por que DynamoDB não tem junções
Um SQL JOIN pede ao banco de dados para ler múltiplas tabelas e montá-las na consulta
tempo. Próprio de AWS
guia para modelagem de dados relacionais
explica o custo: uma consulta como
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é flexível, mas "cada junção na consulta aumenta a complexidade do tempo de execução do consulta porque os dados de cada tabela devem ser preparados e depois montados." Isso o trabalho é ilimitado – seu custo depende dos dados, não da consulta – o que é exatamente a propriedade DynamoDB se recusa a ter.
Então AWS projetou a restrição. DynamoDB é, em suas palavras, "construído para
minimizar as restrições [CPU e rede] eliminando JOINs (e
incentivando a desnormalização dos dados) e otimizando a arquitetura do banco de dados para
responder completamente a uma consulta de aplicativo com uma única solicitação para um item." Esses são
as qualidades que compram latência de um dígito em milissegundos em qualquer escala: o
o custo de tempo de execução de uma leitura DynamoDB é constante, independentemente do tamanho da tabela. Há
nenhum mecanismo de junção e nenhum conceito de chave estrangeira para planejar - por design.
"Mas PartiQL é SQL, certamente ele entra?"
Não. PartiQL fornece a sintaxe SELECT / INSERT / UPDATE / DELETE
DynamoDB, mas é SQL-compatível, não SQL. O
gramática SELECT oficial
é:
SELECT {{expression}} [, ...]
FROM {{table}}[.{{index}}]
[ WHERE {{condition}} ]
[ ORDER BY {{key}} [DESC|ASC], ... ]FROM leva uma tabela (opcionalmente um de seus índices). Não há segundo
Tabela FROM, sem JOIN, sem subconsulta, sem CTE. Corremos todos os três contra DynamoDB
para ver exatamente como cada um falha.
Um JOIN explícito:
SELECT o.pk FROM "Orders" o JOIN "Customers" c ON o.customerId = c.pkValidationException: Only select from a single table or index is supported.Duas tabelas em FROM — mesma rejeição, então o motor não está recusando o JOIN
palavra-chave, está recusando a segunda tabela:
SELECT * FROM "Orders", "Customers"ValidationException: Only select from a single table or index is supported.Uma subconsulta falha de forma diferente, o que vale a pena saber se você estiver depurando uma.
PartiQL não atinge nenhuma verificação de múltiplas tabelas — ele rejeita o operando IN
antes disso, você receberá uma mensagem que nunca menciona tabelas:
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 memberNenhuma frase lhe dá uma adesão. O primeiro dois morrem na segunda tabela e o terceiro morre ainda mais cedo, no operando.
Se você quiser o raciocínio completo sobre por que PartiQL se parece com SQL, mas não consegue se comportar goste, veja PartiQL vs SQL.
As 4 soluções alternativas que os desenvolvedores realmente usam
1. Desnormalizar (copiar os dados)
Armazene os campos que você associaria diretamente ao item. Uma Ordem carrega
um instantâneo de customerName e shippingAddress em vez de um
customerId você resolveria mais tarde. Uma leitura, sem adesão.
A distribuição do tempo de gravação é o custo. Quando a fonte muda você atualiza cada cópia (normalmente através de ummanipulador). Você está negociando complexidade de leitura por complexidade de gravação – geralmente uma boa troca para um aplicativo com muita leitura.
2. Design de tabela única (pré-junção na partição)
Coloque entidades relacionadas em uma tabela sob uma chave de partição compartilhada para que
é o resultado unido. Um cliente e todos os seus pedidos compartilham
PK = "CLIENTE#42"; um Query retorna o item do cliente mais cada pedido
item - a "junção" já aconteceu no momento da gravação.
Query PK = "CUSTOMER#42"
→ CUSTOMER#42 / PROFILE (the customer)
→ CUSTOMER#42 / ORDER#1001 (an order)
→ CUSTOMER#42 / ORDER#1002 (an order)
Esta é a resposta canônica DynamoDB para relacionamentos um-para-muitos. Completo passo a passo em design de tabela única.
3. Junção do lado do aplicativo (duas leituras, costura no código)
Leia a tabela A, pegue as chaves que você recebeu, leia a tabela B e mescle as dois conjuntos de resultados em seu aplicativo. É a lógica de junção relacional - apenas rodando no seu código em vez do banco de dados:
// "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
}));Ótimo para pequenos fan-outs. Com muitos pedidos, torna-se um problema N+1 — uma leitura
para listar pedidos, então uma leitura por pedido - o que é lento e queima a capacidade de leitura.
BatchGetItem (próximo) reduz a segunda onda em uma viagem de ida e volta.
4. BatchGetItem (uma viagem de ida e volta, múltiplas mesas)
BatchGetItem
é o mais próximo que o API chega de "tocar duas mesas ao mesmo tempo": uma
request retorna "os atributos de um ou mais itens de um ou mais
tabelas", até 100 itens ou 16 MB por chamada, o que ocorrer primeiro. Isso
reduz as viagens de ida e volta de uma junção do lado do aplicativo — mas não é uma junção. Você
"identificar itens solicitados por chave primária"; não há condição ON e não
correspondência relacional. Você ainda precisa conhecer as chaves de antemão e costurar o
respostas juntos você mesmo.
Quando um JOIN real é inevitável
As quatro soluções alternativas cobrem bem os caminhos de leitura de produção. Onde eles caem é a consulta ad-hoc, exploratória e analítica — aquela para a qual você não modelou:
- "Quais clientes na UE fizeram um pedido superior a US$ 500 no mês passado?" em um
Tabela
Pedidose uma tabelaClientes. - Uma verificação única da qualidade dos dados que une dois tipos de entidades.
- Relatórios e agregados (
GROUP BY,SUM,COUNT) — que DynamoDB não tem operador para nada.
Estas são exatamente as consultas que você não pode pré-preparar em uma partição, porque
definição, você não sabia que iria perguntar a eles. O instinto relacional - escreva um
JOIN — é o correto aqui. DynamoDB simplesmente não pode atendê-lo nativamente e
nem pode PartiQL.
A resposta usual dos pesos pesados é
exportar para S3 e consultar com Athena
(ou use o conector de consulta federada do Athena para JOIN em uma tabela ativa) ou canalize para
um armazém. Isso é correto para análises verdadeiras em escala, mas é uma
muito encanamento para uma pergunta que você deseja que seja respondida agora, em sua mesa ao vivo.
Executando um JOIN real com DynoTable's SQL Workbench
DynoTable é um cliente DynamoDB de desktop cujo SQL Workbench é executado
SQL real — incluindo JOIN, GROUP BY e funções agregadas — em seu
Tabelas DynamoDB. Ele lê os itens através do DynamoDB API normal, então
executa as partes relacionais da consulta no cliente. Então você pode escrever:
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 obter um conjunto de resultados, em tabelas que não possuem relacionamento definido e um
mecanismo de consulta que não possui palavra-chave
JOIN.
A advertência honesta - "dentro das regras de padrão de acesso de DynamoDB": o Workbench
ainda lê DynamoDB, então uma junção ilimitada é uma leitura ilimitada. O
consultas mais rápidas são aquelas onde a cláusula WHERE (ou o ON da junção
atributo) atinge uma chave de partição ou um GSI em pelo menos um
lado, então DynamoDB executa um Query em vez de uma tabela completa
scan antes da execução da junção. O Workbench não
revogar as restrições deste guia - ele apenas permite que você faça a pergunta SQL
em vez de você mesmo escrever o ponto à mão e dizer o que está fazendo
embaixo.
Entre os clientes GUI, é o único "sim, você pode ingressar" que é realmente verdade: o próprio PartiQL e AWS NãoSQL Workbench — cujo construtor de operações executa operações de tabela única e instruções PartiQL (sem JOIN, sem SELECT de múltiplas mesas) — ambos param na parede de mesa única, assim como a maioria outros clientes GUI. Veja como DynoTable se compara como um DynamoDB GUI.
FAQ
O PartiQL suporta JOIN?
Não. O SELECT de PartiQL lê uma única tabela (ou um de seus índices). Um
consulta multitabela retorna ValidationException: selecione apenas em uma única tabela ou índice é suportado. Mesma parede que o resto do API.
Você pode unir duas tabelas DynamoDB em uma consulta?
Não nativamente. O DynamoDB API não possui nenhuma instrução que leia duas tabelas e corresponda
eles em uma chave. BatchGetItem pode ler itens de múltiplas tabelas em uma solicitação,
mas não tem condição ON - retorna os itens que você nomeou por chave primária e
deixa a correspondência para você. Um verdadeiro JOIN… ON… só acontece fora de DynamoDB:
em seu aplicativo ou no SQL Workbench de DynoTable.
Você pode juntar uma mesa ao seu GSI?
Não — um Índice Secundário Global não é uma tabela separada que você
junte-se a; é uma visão alternativa dos mesmos itens. Você Query qualquer
a tabela ou o índice em um determinado SELECT, não ambos unidos. Um GSI
permite que você alcance itens por uma chave diferente, o que muitas vezes elimina a necessidade de um
junte-se em primeiro lugar.
Você pode ingressar em duas contas AWS (ou duas mesas em contas diferentes)?
Não nativamente — não há nenhuma primitiva de junção entre contas. BatchGetItem pode alcançar
tabela de outra conta se a política baseada em recursos dessa tabela conceder ao chamador
acesso (desde março de 2024), mas ainda não tem condição ON, então é um
leitura de várias tabelas, não uma junção. Você leria cada lado e juntaria os resultados em seu
aplicativo ou em uma ferramenta como DynoTable's Workbench.
A desnormalização é realmente melhor do que uma junção? Para a carga de trabalho alvo do DynamoDB – leituras previsíveis e de alto volume – sim. Você se move o custo para escrever tempo (e aceitar alguma duplicação de dados) em troca de a solicitação única lê essa escala de maneira plana. O O guia design de tabela única cobre as vantagens e desvantagens.
Construir as chaves e condições para essas leituras manualmente é complicado - o
construtor de expressão gera o
Sintaxe KeyConditionExpression / FilterExpression para você, e
DynoTable executa o SQL real quando uma solução alternativa não funciona.
PartiQL mensagens de rejeição reproduzidas em 11/08/2026 contra DynamoDB Local (us-east-1,
@aws-sdk/client-dynamodb v3.1096.0), citado literalmente de ValidationException.message.