n8n-data-tables
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesen8n Data Tables
n8n Data Tables
Data Tables are n8n's built-in tabular storage: real tables inside the n8n instance with columns, types, rows, and CRUD via the node and data-table MCP tools.
dataTableUse them for local persistent state: lookup tables, recent events, per-session inventories, counters, idempotency tracking, dedup state when there's row-level logic or external visibility (plain "have I seen this value?" dedup belongs in the node). Small-to-moderate volume (tens of thousands of rows fine, millions belong in a real DB).
Remove DuplicatesData Tables是n8n的内置表格存储:n8n实例中的真实表格,包含列、类型、行,可通过节点和data-table MCP工具进行CRUD操作。
dataTable可将其用于本地持久化状态:查找表、近期事件、会话级库存、计数器、幂等性追踪、当存在行级逻辑或外部可见性时的去重状态(单纯的「是否见过该值」去重应使用节点)。适用于中小数据量(数万行没问题,数百万行建议使用真实数据库)。
Remove DuplicatesNon-negotiables
必须遵守的准则
- System-managed columns + external IDs. Three columns auto-exist on every table: (bigserial),
id,createdAt. Don't declare them inupdatedAt(errors or shadows the system column). Don't write them on insert. For domain identifiers from outside (arxivId, stripeCustomerId, requestId), add a separate column and key dedup/lookup on that.create_data_table - Only primitives in columns, nested data uses +
stringpostfix. No JSON/object/array column types exist. For nested data (arrays, parsed objects), use a_objectcolumn withstringon write andJSON.stringify(...)on read. Mark the column withJSON.parse(...)(e.g.,_object). The postfix is the contract that tells readers to parse. SeekeyInsights_object.references/SCHEMA_DESIGN.md
- 系统管理列 + 外部ID。每个表格自动存在三列:(bigserial类型)、
id、createdAt。不要在updatedAt中声明这些列(会报错或覆盖系统列)。插入时不要写入这些列。对于外部领域标识符(如arxivId、stripeCustomerId、requestId),添加单独的列,并基于该列进行去重/查找。create_data_table - 列中仅存储基础类型,嵌套数据使用类型 +
string后缀。不存在JSON/对象/数组列类型。对于嵌套数据(数组、解析后的对象),写入时使用_object转换为字符串,读取时使用JSON.stringify(...)解析。列名需添加JSON.parse(...)后缀(例如_object)。该后缀是约定,用于提示读者需要解析该列。详情见keyInsights_object。references/SCHEMA_DESIGN.md
Strong defaults
最佳默认做法
- Don't add a Set node before a Data Table node to modify fields. The Data Table node's per-column expression slots are just as powerful as Set fields, so the Set node is doing zero work the Data Table node can't do itself. (Same Set-node antipattern called out in .)
n8n-expressions - Match n8n's column casing: camelCase. The auto-managed columns are camelCase (,
createdAt), so user columns read more cleanly when they match:updatedAt,arxivId,paperId. Mixed casing in the same query (taxRate) reads as a typo. Keep thecreatedAt >= ... AND arxiv_id eq ...postfix on stringified-blob columns regardless (_object), the underscore is a contract marker, not casing.keyInsights_object
- Verify the parameter via
columnsafter create/update. The UI has a display quirk in manual mapping mode ("Currently no items exist" with no actual data loss). Checking the JSON confirms what's persisted.get_workflow_details - Relational design works when the shape calls for it. For genuine parent-child data (papers → summaries, customers → orders), reference parents by , name columns explicitly (
id,paperId), and enforce integrity in workflow logic. Don't force it on flat use cases (dedup, lookup, audit) where there's no relationship to model.customerId - Storage format is not interface format. Parse fields before returning them from a sub-workflow. Callers should never receive stringified shells they have to parse themselves. See
_object"Storage format ≠ interface format".references/SCHEMA_DESIGN.md
- 不要在Data Table节点前添加Set节点来修改字段。Data Table节点的每列表达式槽与Set字段功能同样强大,因此Set节点无法完成任何Data Table节点本身做不到的工作(中也提到了这种Set节点反模式)。
n8n-expressions - 匹配n8n的列命名规范:camelCase。系统自动管理的列采用camelCase格式(、
createdAt),因此用户自定义列使用相同格式时可读性更强:updatedAt、arxivId、paperId。同一查询中混用大小写(如taxRate)会看起来像拼写错误。无论如何,字符串化二进制大对象列需保留createdAt >= ... AND arxiv_id eq ...后缀(如_object),下划线是约定标记,不属于命名规范。keyInsights_object
- 创建/更新后通过验证
get_workflow_details参数。手动映射模式下UI存在显示异常(「当前无数据项」但实际无数据丢失)。查看JSON可确认持久化的内容。columns - 当数据结构需要时使用关系型设计。对于真正的父子数据(论文→摘要、客户→订单),通过引用父级,明确命名列(
id、paperId),并在工作流逻辑中保证数据完整性。不要在无关系可建模的扁平场景(去重、查找、审计)中强行使用。customerId - 存储格式≠接口格式。从子工作流返回数据前,需解析字段。调用方不应收到需要自行解析的字符串化数据壳。详情见
_object中的「存储格式≠接口格式」部分。references/SCHEMA_DESIGN.md
The default columns
默认列
Every Data Table has these whether you declare them or not:
| Column | Type | Behavior |
|---|---|---|
| bigserial / number | Auto-incrementing primary key. n8n assigns on insert, and you can't write to it. Returned in the insert response. |
| timestamp | Set automatically on insert. |
| timestamp | Refreshed automatically on each update. |
In practice:
- Don't declare them in . Already there.
create_data_table - Use them in queries without your own timestamp columns. "Created today": . "Updated since last sync":
createdAt >= '<today ISO>'.updatedAt >= $('Last Sync').item.json.timestamp - Don't use them as cross-system identifiers. Auto-is internal, and resets on table recreate or instance migration. For domain identifiers, use your own column.
id
无论是否声明,每个Data Table都包含以下列:
| 列名 | 类型 | 行为 |
|---|---|---|
| bigserial / 数字 | 自增主键。n8n在插入时自动分配,无法手动写入。会在插入响应中返回。 |
| 时间戳 | 插入时自动设置。 |
| 时间戳 | 每次更新时自动刷新。 |
实际使用注意事项:
- 不要在中声明这些列,它们已经存在。
create_data_table - 查询时使用这些列,无需自行创建时间戳列。例如「今日创建」:;「上次同步后更新」:
createdAt >= '<今日ISO格式>'。updatedAt >= $('Last Sync').item.json.timestamp - 不要将这些列用作跨系统标识符。自动生成的是内部标识,在表格重建或实例迁移时会重置。对于领域标识符,使用自定义列。
id
Relational design when the shape calls for it
数据结构需要时的关系型设计
Data Tables don't enforce foreign keys, but you can still model parent-child data across tables when the data genuinely has that shape. The catch: integrity is your responsibility, not n8n's.
- Reference parent rows by . A child table holds the parent's
idin a column.id - Document references in column names. ,
paperId,customerIdmake the relationship obvious.eventId - Enforce integrity in workflow logic. Before inserting a child, look up the parent. Before deleting a parent, decide what happens to children (delete, orphan, archive). n8n won't cascade.
- Watch for stale references. Children pointing at deleted parents are silent bugs. Soft-delete, or run cleanup workflows.
For complex relational structure (3+ tables with joins, transactional writes), reach for an actual SQL DB.
Data Tables不支持外键约束,但当数据确实存在父子结构时,仍可跨表建模父子数据。注意:数据完整性需由您负责,而非n8n。
- 通过引用父行。子表中用一列存储父级的
id。id - 在列名中明确标注引用关系。、
paperId、customerId可让关系一目了然。eventId - 在工作流逻辑中保证数据完整性。插入子数据前,先查找父数据。删除父数据前,确定子数据的处理方式(删除、保留、归档)。n8n不会自动级联操作。
- 注意过时引用。指向已删除父数据的子数据会导致隐性bug。可使用软删除,或运行清理工作流。
对于复杂的关系结构(3个及以上表的关联、事务性写入),建议使用真实的SQL数据库。
Operations: which one for what
操作场景匹配
| Operation | When |
|---|---|
| Always-add. New row, n8n assigns |
| "Add if new, update if exists." Needs a |
| "Modify rows matching this filter." No insert if no match. |
| Fetch rows matching a filter (returns 0+). Supports |
| Remove rows matching a filter. |
| Boolean-style filter against incoming items. Common for dedup branching. |
For the full operation surface (filter syntax, matchType, sort patterns), see .
<!-- TEMPORARY: remove when the data tables node quirk is fixed -->references/OPERATIONS.md| 操作 | 使用场景 |
|---|---|
| 仅新增。生成新行,n8n自动分配 |
| 「不存在则新增,存在则更新」。需要 |
| 「修改匹配过滤器的行」。无匹配项时不会插入。 |
| 获取匹配过滤器的行(返回0行及以上)。支持 |
| 删除匹配过滤器的行。 |
| 对输入项进行布尔值过滤。常用于去重分支判断。 |
完整操作说明(过滤器语法、matchType、排序模式)见。
<!-- 临时内容:修复Data Tables节点异常后移除 -->references/OPERATIONS.mdThe "Currently no items exist" UI quirk
「当前无数据项」UI显示异常
When the SDK saves manual-mode column mappings (), the n8n UI's "Values to insert" pane can render empty ("Currently no items exist") even though runtime persists data correctly. If the user reports the Insert node "looks broken" or "has no fields," tell them: it's a UI display issue, press the reload (refresh) button on the columns parameter, and it repopulates the schema and the mappings render. No data loss, safe to do anytime.
<!-- TEMPORARY: SDK-saved defineBelow column mappings can render as "Currently no items exist" in the n8n UI until the user clicks the reload button on the columns parameter. Runtime persistence unaffected. Remove this section once n8n auto-refreshes the schema on workflow load. -->mappingMode: 'defineBelow'当SDK保存手动模式列映射()时,n8n UI的「要插入的值」面板可能显示为空(「当前无数据项」),但运行时实际已正确持久化数据。如果用户反馈Insert节点「看起来坏了」或「没有字段」,请告知:这是UI显示问题,点击columns参数上的刷新按钮,即可重新加载schema并显示映射。无数据丢失,任何时候操作都安全。
<!-- 临时内容:SDK保存的defineBelow列映射在n8n UI中会显示为「当前无数据项」,直到用户点击columns参数的刷新按钮。运行时持久化不受影响。n8n实现工作流加载时自动刷新schema后移除本部分。 -->mappingMode: 'defineBelow'Common patterns
常见模式
Dedup by external ID
基于外部ID去重
Default to the node ("items seen in previous executions" mode) for plain "have I seen this value?" dedup. It's a one-node solution with an internal store, no schema to maintain. Data Tables only earn the slot when there's a reason for the dedup state to live in a real table:
Remove Duplicates- You'll query or inspect the dedup state. Dashboards, audit, "what have we processed in the last week?"
- Row-level logic on hits. Per-category TTL ("expire after 30 days for category A, 7 days for category B"), conditional re-process based on stored state, branching on a status column.
- Per-tenant or per-user namespacing that the history-store can't express.
Remove Duplicates
When that bar is met:
[Source: { arxivId, ... }]
↓
[Data Table Get: filter arxivId eq $json.arxivId, limit 1]
↓
[IF: result has items?]
├── Yes → [Skip, or apply row-level logic from the stored row]
└── No → [Process] → [Data Table Insert: { arxivId, ...rest }]For the full pattern surface (upsert, rowNotExists, Get+IF, idempotency keys), see .
references/DEDUP_PATTERNS.md对于单纯的「是否见过该值」去重,默认使用节点(「之前执行中见过的项」模式)。这是无需维护schema的单节点解决方案,内部自带存储。只有当去重状态需要存储在真实表格中时,才使用Data Tables:
Remove Duplicates- 需要查询或检查去重状态:如仪表盘、审计、「过去一周处理过哪些内容?」
- 命中时需执行行级逻辑:按类别设置TTL(「A类30天后过期,B类7天后过期」)、基于存储状态有条件地重新处理、基于状态列分支。
- 需要按租户或用户隔离,而的历史存储无法实现该需求。
Remove Duplicates
满足上述条件时,使用以下流程:
[数据源: { arxivId, ... }]
↓
[Data Table Get: 过滤器 arxivId eq $json.arxivId, limit 1]
↓
[条件判断: 是否有结果?]
├── 是 → [跳过,或基于存储行执行行级逻辑]
└── 否 → [处理] → [Data Table Insert: { arxivId, ...rest }]完整模式说明(upsert、rowNotExists、Get+条件判断、幂等键)见。
references/DEDUP_PATTERNS.mdLookup tables
查找表
Stable reference data (country → tax rate, plan → feature flags). Edited via n8n UI, and workflows read at execution:
[Data Table Get: filter country eq $json.country, limit 1]
↓
[Use the looked-up row's taxRate, etc.]稳定的参考数据(国家→税率、套餐→功能标志)。可通过n8n UI编辑,工作流执行时读取:
[Data Table Get: 过滤器 country eq $json.country, limit 1]
↓
[使用查找行的taxRate等数据]Recent events / audit trail
近期事件/审计追踪
Append-only insert, queried later:
[Workflow event] → [Data Table Insert: { userId, eventType, payloadSummary }]createdAt仅追加插入,后续查询:
[工作流事件] → [Data Table Insert: { userId, eventType, payloadSummary }]createdAtReference files
参考文档
| File | Read when |
|---|---|
| Designing columns/types, the no-FK relational pattern, mapping mode ( |
| Operation surface (insert/upsert/update/get/delete/rowExists), filter syntax, matchType, orderBy |
| Idempotency keys, RemoveDuplicates node vs Data Table dedup, search-then-insert vs upsert |
For expression discipline ( vs , the Set-node antipattern), see . For Merge convergence and same-shape branches, see .
$json$('Node Name').item.jsonn8n-expressionsn8n-connections| 文件 | 阅读场景 |
|---|---|
| 设计列/类型、无外键的关系型模式、映射模式( |
| 操作说明(insert/upsert/update/get/delete/rowExists)、过滤器语法、matchType、orderBy |
| 幂等键、RemoveDuplicates节点vs Data Table去重、先搜索后插入vs upsert |
表达式规范( vs 、Set节点反模式)见。合并收敛和同结构分支见。
$json$('Node Name').item.jsonn8n-expressionsn8n-connectionsAnti-patterns
反模式
| Anti-pattern | What goes wrong | Fix |
|---|---|---|
| Set node upstream of Insert "to shape the input" | Extra node for nothing, classic Set antipattern, field shape drifts when you add columns | Map directly in the Insert node's per-column slots, OR rename upstream fields to enable auto-map |
Declaring | Errors, or shadows the system column with a user column that doesn't auto-update | Don't declare them, they're already there |
| Storing application-critical data in Data Tables | If n8n breaks, you lose access | Use a real DB for data you can't lose |
| Cross-app system-of-record in Data Tables | Hard to share with non-n8n consumers, awkward query surface | Use a real DB |
Treating auto- | Resets if the table is recreated, not portable | Use a domain ID column ( |
| Foreign-key cascade assumptions | n8n doesn't cascade, deleted parents leave orphan children | Soft-delete, or run cleanup workflows that maintain referential integrity |
| Referencing an immediately-prior node when an intermediate stripped json | Insert silently writes NULLs for fields that "should be there" | Reference a stable upstream node by name, or use a NoOp/Merge convergence anchor (see |
| Manual-map mode + Set node to fix "Currently no items exist" | Doesn't fix anything, that's a UI quirk, you've added a useless Set node | Verify via |
| 反模式 | 问题 | 修复方案 |
|---|---|---|
| Insert节点上游使用Set节点「调整输入格式」 | 多余节点,典型的Set反模式,添加列时字段格式会不一致 | 直接在Insert节点的每列槽中映射,或重命名上游字段以启用自动映射 |
在 | 报错,或用用户列覆盖系统列且无法自动更新 | 不要声明这些列,它们已存在 |
| 在Data Tables中存储应用核心数据 | n8n故障时会丢失数据访问权限 | 对于不可丢失的数据,使用真实数据库 |
| 将Data Tables作为跨应用的记录系统 | 非n8n使用者难以共享,查询体验不佳 | 使用真实数据库 |
将自动生成的 | 表格重建时会重置,不具备可移植性 | 使用领域ID列( |
| 假设外键会自动级联 | n8n不支持级联操作,删除父数据会留下孤立子数据 | 使用软删除,或运行维护引用完整性的清理工作流 |
| 中间节点剥离JSON时引用紧邻的前一个节点 | Insert会静默写入「应该存在」的字段为NULL | 通过名称引用稳定的上游节点,或使用NoOp/Merge收敛锚点(见 |
| 手动映射模式下使用Set节点修复「当前无数据项」问题 | 无任何作用,这是UI显示异常,添加了无用的Set节点 | 通过 |
Verification before publishing
发布前验证
After creating or updating a workflow that uses Data Tables:
- passes.
validate_workflow - and inspect each Data Table node's
get_workflow_details. Bothcolumnsand (for manual map)valuepopulated.schema - with pinned data. Insert response should include
test_workflow,id,createdAt.updatedAt - Inspect actual Data Table contents via UI or follow-up Get to confirm columns aren't silently NULL.
Step 4 especially the first time you wire a new Insert. Context-stripping intermediates + manual map + UI quirk silently produce NULL columns.
创建或更新使用Data Tables的工作流后:
- 验证通过。
validate_workflow - 调用并检查每个Data Table节点的
get_workflow_details。columns和(手动映射模式下的)value均已填充。schema - 使用固定数据运行。Insert响应应包含
test_workflow、id、createdAt。updatedAt - 通过UI或后续Get操作检查Data Table实际内容,确认列未被静默设置为NULL。
首次配置新Insert时,步骤4尤为重要。上下文剥离的中间节点+手动映射+UI异常可能会静默生成NULL列。