数据库
在 Celigo RDBMS 导入导出中编写或排查…
适用于编辑 Celigo RDBMS 导出/导入的 `rdbms.query` 或排查 SQL 错误,覆盖 SELECT、INSERT、UPDATE、UPSERT、MERGE、delta、once 和批量操作及多数据库方言。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Celigo RDBMS 导入导出中特定
解决思路
- 识别当前 SQL 所处的上下文:导出 `rdbms.query`、导出 delta 查询 `rdbms.query`、导出 once 标记 `rdbms.once.query`、导入 per_record `rdbms.query[]`、导入 per_page `rdbms.query[]`、导入 bulk_load override `rdbms.bulkLoad.overrideMergeQuery`;按对应 Handlebars 上下文和字段前缀写值。
- 导出普通查询使用 `record.` 前缀(参数化)或无前缀(独立);用 `{{{ }}}`,例如 `SELECT * FROM orders WHERE id = {{{record.id}}}`。
- 导出 delta 查询使用平台注入 token,用 `{{ }}` 或 `{{{ }}}`,例如 `WHERE updated_at > '{{lastExportDateTime}}'`。
- 导出 once 标记查询在 `rdbms.once.query` 中用 `record.` 和 `{{{ }}}`,例如 `UPDATE orders SET exported = true WHERE id = {{{record.id}}}`。
- 导入 per_record 查询在 `rdbms.query[]` 中用 `record.` 和 `{{{ }}}`,例如 `INSERT INTO users (name) VALUES ('{{{record.name}}}')`。
- 导入 per_page 查询在 `rdbms.query[]` 中用 `batch_of_records` 和 `record.`,用 `{{{ }}}`,例如 `{{#each batch_of_records}}...{{{record.field}}}...{{/each}}`。
- 导入 bulk_load override 查询在 `rdbms.bulkLoad.overrideMergeQuery` 中用 staging 表引用和 `{{ }}`,例如 `MERGE INTO target USING {{import.rdbms.bulkLoad.preMergeTemporaryTable}}`。
- 始终使用 `record.` 前缀(AFE 2.0),不要用裸 `fieldName` 或 `data.fieldName`。
- SQL 中的值优选用三花括号 `{{{ }}}`;双花括号 `{{ }}` 在 RDBMS 上下文中会自动把值包在单引号里,会静默破坏数值并破坏 SQL 语法。
- 字符串自己加引号:`'{{{record.name}}}'`;三花括号让你完全控制引号。
- 数字不要加引号:`{{{record.quantity}}}`。
- 导入 `query` 是字符串数组,不是单字符串:第一个元素可为 `INSERT INTO ...`。
- 嵌套字段用点号:`{{{record.address.city}}}`。
- 需要 adaptorType、delta/once 模式、导出级配置时查阅 configuring-exports > RDBMS;需要 queryType 决策树、bulkInsert/bulkLoad 配置时查阅 configuring-imports > RDBMS;需要完整 Handlebars 语法/helper/块表达式时查阅 writing-handlebars;需要数据库连接设置时查阅 configuring-connections > RDBMS。
- 需要导出/导入 schema 时查阅 references/schemas/rdbms-export.yml 和 references/schemas/rdbms-import.yml。
- 写 SQL 前先查询线上数据库发现表和列:运行 `celigo metadata types <connectionId>` 列出表,运行 `celigo metadata fields <connectionId> <tableName>` 列出列和类型。
- 对 Snowflake 使用全限定名 `database.schema.table`。
- 导出全量抓取:`type` 为 null,查询模板 `SELECT columns FROM table WHERE conditions`。
- 导出 delta/增量:`type` 为 delta,查询 `SELECT ... WHERE updated_at > '{{lastExportDateTime}}'`。
- 导出 once:`type` 为 once,`rdbms.query` 为 SELECT 未处理记录,`rdbms.once.query` 为 UPDATE 标记已处理。
- 导入 INSERT 且查重:`queryType` 用 per_record,在 `rdbms.query` 中写 INSERT ... ON CONFLICT / ON DUPLICATE KEY。
- 导入 UPDATE:`queryType` 用 per_record,在 `rdbms.query` 中写 UPDATE ... WHERE。
- 导入 UPSERT / MERGE:`queryType` 用 per_record 或 bulk_load;per_record 在 `rdbms.query` 中写 MERGE;bulk_load 配置 `bulkLoad.primaryKeys`。
- 导入纯 INSERT 不做查重:`queryType` 用 bulk_insert,配置 `rdbms.bulkInsert.tableName` 和 `batchSize`,无需写 SQL。
- 批量加载并 upsert:`queryType` 用 bulk_load,配置 `rdbms.bulkLoad.tableName` 和 `primaryKeys`,平台自动生成 MERGE。
- 自定义批量 merge:`queryType` 用 bulk_load,设置 `bulkLoad.overrideMergeQuery: true` 并写引用 staging 表的自定义 SQL。
- 按目标数据库方言写 SQL,运行时值用 `{{{record.fieldName}}}`。
- 测试导出查询:运行 `celigo exports invoke <exportId>`,invoke 返回真实数据。
- 测试导入:运行 `echo '[{"name":"test","email":"test@example.com"}]' | celigo imports invoke <importId>` 提交测试记录。
- 导出标准 SELECT 按示例结构写:`SELECT id, name, email, status FROM customers WHERE status = 'ACTIVE' ORDER BY id`。
- delta 导出使用 `{{lastExportDateTime}}`(平台注入,不来自 record);该 token 解析为最近一次成功导出的时间戳。
- delta 的时间格式按数据库调整:Snowflake 用 `TO_TIMESTAMP('{{lastExportDateTime}}', 'YYYY-MM-DD HH24:MI:SS')`;MySQL 用 `STR_TO_DATE('{{lastExportDateTime}}', '%Y-%m-%d %H:%i:%s')`;PostgreSQL 用 `'{{lastExportDateTime}}'::timestamp`。
- once 导出要让两个查询配合:`rdbms.query` 抓取未处理记录;`rdbms.once.query` 在每条成功导出后标记。
- 导出 JOIN 要写别名并限定列,例如 `FROM orders o JOIN customers c ON o.customer_id = c.id ... WHERE o.status = 'SHIPPED' AND o.order_date > '{{lastExportDateTime}}'`。
- 导出聚合用 GROUP BY 和 HAVING,例如 `SELECT customer_id, COUNT(*) AS order_count, SUM(total) AS lifetime_value FROM orders WHERE status IN ('COMPLETED', 'SHIPPED') GROUP BY customer_id HAVING SUM(total) > 1000`。
- 所有导入查询都用 `{{{record.fieldName}}}` 从传入记录注入值;`query` 字段是字符串数组。
- INSERT per_record:设置 `rdbms.queryType` 为 per_record,写 `INSERT INTO customers (name, email, phone) VALUES ('{{{record.name}}}', '{{{record.email}}}', '{{{record.phone}}}')`。
- UPDATE per_record:设置 `rdbms.queryType` 为 per_record,写 `UPDATE customers SET name = '{{{record.name}}}', email = '{{{record.email}}}' WHERE id = {{{record.id}}}`。
- MySQL UPSERT:用 `ON DUPLICATE KEY UPDATE`,例如 `INSERT INTO customers (id, name, email) VALUES ({{{record.id}}}, '{{{record.name}}}', '{{{record.email}}}') ON DUPLICATE KEY UPDATE name = '{{{record.name}}}', email = '{{{record.email}}}'`。
- PostgreSQL UPSERT:用 `ON CONFLICT`,例如 `INSERT INTO customers (id, name, email) VALUES ({{{record.id}}}, '{{{record.name}}}', '{{{record.email}}}') ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, email = EXCLUDED.email`。
- Snowflake UPSERT:用 `MERGE`,例如 `MERGE INTO customers AS t USING (SELECT {{{record.id}}} AS id, '{{{record.name}}}' AS name, '{{{record.email}}}' AS email) AS s ON t.id = s.id WHEN MATCHED THEN UPDATE SET t.name = s.name, t.email = s.email WHEN NOT MATCHED THEN INSERT (id, name, email) VALUES (s.id, s.name, s.email)`。
- SQL Server UPSERT:用 `MERGE`,并在末尾加分号,例如 `MERGE INTO customers AS t USING (SELECT ...) AS s ON t.id = s.id WHEN MATCHED THEN UPDATE SET ... WHEN NOT MATCHED THEN INSERT ...;`。
- per_record 需要每条记录执行多条 SQL 时,把多条语句作为 `query` 数组的不同元素,例如先 `INSERT INTO orders ...`,再 `UPDATE customers SET last_order_date = CURRENT_TIMESTAMP WHERE id = {{{record.customerId}}}`。
- 纯 INSERT 无需 SQL:设置 `queryType` 为 bulk_insert,配置 `bulkInsert.tableName` 和 `bulkInsert.batchSize`;平台生成 SQL。
- bulkInsert 列映射来自导入的 `mappings[]`(Mapper 2.0),每个 mapping 的 `generate` 值必须匹配目标表列名。
- 高吞吐 upsert(Snowflake 或 Azure Synapse)用 `bulkLoad` + `primaryKeys`:设置 `queryType` 为 bulk_load,配置 `bulkLoad.tableName` 和 `bulkLoad.primaryKeys`;平台把数据落到临时表,再用主键自动生成 MERGE。复合键写 `primaryKeys` 为 `order_id,product_id`。
- 自动生成 MERGE 不够时(条件更新、忽略已存在、多表操作)覆盖它:设置 `bulkLoad.overrideMergeQuery: true`,自定义 SQL 放在 `rdbms.query`,通过 `{{import.rdbms.bulkLoad.preMergeTemporaryTable}}` 引用 staging 表,例如 `MERGE INTO WAREHOUSE.PUBLIC.CUSTOMERS AS t USING {{import.rdbms.bulkLoad.preMergeTemporaryTable}} AS s ON t.id = s.id WHEN MATCHED AND s.updated_at > t.updated_at THEN UPDATE SET t.name = s.name, t.email = s.email, t.updated_at = s.updated_at WHEN NOT MATCHED THEN INSERT (id, name, email, updated_at) VALUES (s.id, s.name, s.email, s.updated_at)`。
- per_page 批处理:`queryType` 为 per_page 时整个页面记录在 `batch_of_records`;用 `{{#each batch_of_records}}('{{{record.name}}}', '{{{record.email}}}'){{#unless @last}},{{/unless}}{{/each}}` 迭代生成多值 INSERT。
- Snowflake:除非连接设置默认 schema,否则必须用全限定表名 `MY_DATABASE.MY_SCHEMA.MY_TABLE`。
- Snowflake:半结构化 VARIANT 列用 `LATERAL FLATTEN(input => MY_TABLE.json_column)` 和 `f.value:name::STRING` 取值。
- Snowflake:窗口函数过滤用 `QUALIFY`,例如 `QUALIFY ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) = 1`。
- Snowflake:时间戳用 `TO_TIMESTAMP('{{lastExportDateTime}}', 'YYYY-MM-DD"T"HH24:MI:SS')`。
- Snowflake:未加引号的标识符会转大写;要保留大小写用英文双引号包裹标识符。
- PostgreSQL:JSONB 用 `data->>'name'`、`data->'address'->>'city'`、`data @> '{"active": true}'`。
- PostgreSQL:大小写不敏感匹配用 `ILIKE`,如 `name ILIKE '%widget%'`。
- PostgreSQL:数组操作用 `'admin' = ANY(roles)`。
- PostgreSQL:CTE 用 `WITH recent_orders AS (SELECT * FROM orders WHERE order_date > '{{lastExportDateTime}}') SELECT c.name, r.total FROM customers c JOIN recent_orders r ON c.id = r.customer_id`。
- MySQL/MariaDB:JSON 列用 `JSON_EXTRACT(data, '$.name')`。
- MySQL/MariaDB:字符串聚合用 `GROUP_CONCAT(product_name SEPARATOR ', ')`。
- MySQL/MariaDB:空值处理用 `IFNULL(middle_name, '')`。
- SQL Server/Azure Synapse:MERGE 必须以分号结尾。
- SQL Server/Azure Synapse:限制行数用 `TOP` 而不是 `LIMIT`,如 `SELECT TOP 100 * FROM orders ORDER BY order_date DESC`。
- SQL Server/Azure Synapse:字符串聚合用 `STRING_AGG(product_name, ', ')`(SQL Server 2017+)。
- SQL Server/Azure Synapse:行值函数用 `CROSS APPLY`,如 `CROSS APPLY (SELECT TOP 1 * FROM orders WHERE customer_id = c.id ORDER BY order_date DESC) o`。
- SQL Server/Azure Synapse:保留字或特殊字符标识符用方括号,如 `SELECT [order], [name] FROM [my-table]`。
- Oracle:空值处理用 `NVL(middle_name, '')`。
- Oracle:12c 前限制行数用 `ROWNUM`,如 `SELECT * FROM (SELECT * FROM orders ORDER BY order_date DESC) WHERE ROWNUM <= 100`。
- Oracle:12c+ 限制行数用 `FETCH FIRST 100 ROWS ONLY`。
- Oracle:字符串聚合用 `LISTAGG(product_name, ', ') WITHIN GROUP (ORDER BY product_name)`。
- Oracle:MERGE 用 `USING (SELECT ... FROM dual) s`,例如 `MERGE INTO customers t USING (SELECT {{{record.id}}} AS id, '{{{record.name}}}' AS name FROM dual) s ON (t.id = s.id) WHEN MATCHED THEN UPDATE SET t.name = s.name WHEN NOT MATCHED THEN INSERT (id, name) VALUES (s.id, s.name)`。
- BigQuery:标识符用反引号,如 `SELECT * FROM `project.dataset.table``。
- BigQuery:数组列用 `UNNEST(items) AS item`。
- BigQuery:STRUCT 用 `address.city, address.state` 访问。
- BigQuery:避免除零用 `SAFE_DIVIDE(revenue, orders)`。
- BigQuery:时间戳用 `TIMESTAMP('{{lastExportDateTime}}')`。
- Redshift:使用 PostgreSQL 风格语法,如 `SELECT * FROM orders WHERE status = 'ACTIVE'`。
- Redshift:字符串聚合用 `LISTAGG(product_name, ', ') WITHIN GROUP (ORDER BY product_name)`。
- Redshift:`bulk_load` 底层用 Redshift COPY;自定义 merge 逻辑用 `overrideMergeQuery` 和 staging 表引用。
- 提交前检查导出查询:SELECT 对目标数据库方言语法有效。
- 提交前检查 delta:在查询中使用 `{{lastExportDateTime}}`,不要把 delta 对象放在 `rdbms` 内。
- 提交前检查 once 导出:同时有 `rdbms.query` 的 SELECT 和 `rdbms.once.query` 的 UPDATE。
- 提交前检查表和列存在:用 `celigo metadata types/fields <connectionId>` 验证。
- 提交前检查 Snowflake 全限定名:除非连接设置默认值,否则用 `database.schema.table`。
- 提交前检查导入:`query` 是字符串数组,数组元素为 `INSERT INTO ...`,不是单字符串 `INSERT INTO ...`。
- 提交前检查导入:`queryType` 匹配操作 -- UPDATE/UPSERT 用 per_record,纯 INSERT 用 bulk_insert,高吞吐用 bulk_load。
- 提交前检查导入:所有 `{{{record.fieldName}}}` 路径匹配传入数据 -- invoke 上游导出验证字段名。
- 提交前检查导入:字符串加引号,数字不加引号。
- 提交前检查导入:使用 `record.` 前缀,不是裸 `fieldName` 或 `data.fieldName`。
- 提交前检查导入:使用三花括号 `{{{ }}}`,双花括号会自动加引号并破坏数值和 SQL 语法。
- 提交前检查导入:`bulkInsert`/`bulkLoad` 不要和 `query` 字段同时设置;它们互斥(`overrideMergeQuery` 除外)。
- 跨资源检查:连接 `type` 是 RDBMS 之一:mysql、mariadb、postgresql、mssql、azuresynapse、oracle、snowflake、bigquery、redshift。
- 跨资源检查:SQL 方言匹配数据库 -- Snowflake、SQL Server、Oracle、PostgreSQL 的 MERGE 语法不同。
- 避免 gotcha:双花括号在 RDBMS 中自动格式化:`{{record.name}}` 输出带单引号的 `'value'`,`{{{record.name}}}` 输出原始 `value`;用三花括号并自己给字符串加引号。
- 避免 gotcha:导入 `query` 必须是数组;`query` 为单字符串会失败或抛 Cast error;始终写字符串数组。
- 避免 gotcha:`queryType` 值必须具体:per_record、bulk_insert、per_page、bulk_load;不要用 INSERT 或 UPDATE 作为独立值。
- 避免 gotcha:Snowflake 在 PUT 时拒绝旧 `queryType` 值;`insert`/`update` 可能在 POST 能用但 PUT 失败;一开始就用 `per_record` 或 `bulk_insert`。
- 避免 gotcha:缺少 `record.` 前缀会产生空值;`{{{name}}}` 解析不到东西;始终用 `{{{record.name}}}`。
- 避免 gotcha:Snowflake 要求全限定表名 `database.schema.table`;未限定会静默失败或打到错误表。
- 避免 gotcha:`per_page` 的 Handlebars 上下文是 `batch_of_records`,不是单条记录;用 `{{#each batch_of_records}}...{{{record.fieldName}}}...{{/each}}`。
- 避免 gotcha:`bulkLoad.overrideMergeQuery` 引用 staging 表,用 `{{import.rdbms.bulkLoad.preMergeTemporaryTable}}`,不是目标表名。
- 避免 gotcha:SQL Server MERGE 末尾必须加分号;缺少 `;` 会语法错误。
- 避免 gotcha:`once.query` 按记录运行,不是按批;其中的 `{{record.id}}` 指当前导出记录;不要写批量 UPDATE。
- 避免 gotcha:不要把 delta 配置放进 `rdbms`;没有 `rdbms.delta` 属性;delta 通过在 SQL 文本中嵌入 `{{lastExportDateTime}}` 实现。
- 避免 gotcha:NULL 处理因方言而异:Oracle 用 `NVL`,MySQL 用 `IFNULL`,标准/Snowflake/PostgreSQL/SQL Server 用 `COALESCE`;不要假设一个到处可用。
- 常见错误修复:`Cast error` 或 `Invalid query format` -> `query` 是字符串不是数组 -> 改成字符串数组,元素为 `SQL here`。
- 常见错误修复:SQL 中空值/期望数据处为 `NULL` -> Handlebars 缺少 `record.` 前缀 -> 用 `{{{record.fieldName}}}`。
- 常见错误修复:数值字段变成 `'42'` 而不是 `42` -> 双花括号自动加引号 -> 改用三花括号 `{{{ }}}`。
- 常见错误修复:Snowflake `Compilation error: Object does not exist` -> 未限定表名 -> 用 `database.schema.table`。
- 常见错误修复:PUT 时 `Invalid value for queryType` -> 旧 `insert`/`update` queryType -> 用 `per_record` 或 `bulk_insert`。
- 常见错误修复:SQL Server `Merge statement must be terminated by ;` -> MERGE 末尾缺分号 -> 在最后子句后加 `;`。
- 常见错误修复:`Ambiguous column reference` -> JOIN 没有表别名 -> 用表别名限定列。
- 常见错误修复:`Syntax error near ON DUPLICATE KEY` -> 在 PostgreSQL/Snowflake 用了 MySQL 语法 -> 用正确方言:PostgreSQL 用 `ON CONFLICT`,Snowflake 用 `MERGE`。
- 常见错误修复:`Column count doesn't match value count` -> INSERT 列和 VALUES 数量不匹配 -> 核对列列表和 `{{{record.x}}}` 值数量。
- 常见错误修复:`Permission denied for table` -> 连接用户缺少 INSERT/UPDATE 授权 -> 检查连接用户的数据库权限。
适用边界 · 注意事项
- 不适用于非 Celigo RDBMS 导入导出场景,或没有 Celigo 平台/CLI 的环境。
- 不适用于需要选择 adaptorType、配置连接或决定 queryType 本身的场景;这些应查阅 configuring-exports、configuring-imports、configuring-connections 等相关技能。
- 不适用于非关系型数据库或非 SQL 数据源。