跳至內容
Astera Technology

搬移子行為何連到不相關的現有父紀錄?

來源參考須按真正分配的目標編號對應再寫入。

下載完整個案資料及參考答案 ↓

先核來源與已訂規則

本例來源父 7 搬入已有 7、100 的目標,新分配編號是 101。子行來源 7 指這個搬入父,不是目標舊 7。例子先建搬入父的暫存行,子參考未有對應便暫停。

已填假設輸入
紀錄輸入 JSON
原例輸入{"existing":[7,100],"sourceId":7,"childSource":7}
下載可編輯 CSV ↓

比較兩條實際路徑

錯子連 7,因舊目標 7 存在,普通外鍵仍可過,卻連錯對象。更正要沿來源 7→目標 101,子連 101。

錯誤與更正路徑的輸出
路徑輸出 JSON
錯誤路徑{"mapping":{"source":7,"target":101},"error":null,"parents":[{"id":7,"label":"Existing"},{"id":100,"label":"Existing"},{"id":101,"label":"Imported"}],"children":[{"parent_id":7}]}
更正路徑{"mapping":{"source":7,"target":101},"error":null,"parents":[{"id":7,"label":"Existing"},{"id":100,"label":"Existing"},{"id":101,"label":"Imported"}],"children":[{"parent_id":101}]}
下載可編輯 CSV ↓
本地 Node.js 程序:22.23.3 或 24.14.1
import {DatabaseSync} from 'node:sqlite';
import {TextDecoder} from 'node:util';
import {inflateSync} from 'node:zlib';
import {posix} from 'node:path';
import {createHash} from 'node:crypto';
const rows=(db,sql)=>db.prepare(sql).all().map(r=>({...r}));
const failed=(action)=>{try{action();return null;}catch(e){return {code:e.errcode??e.code??e.name};}};

import assert from 'node:assert/strict';
const run=function run(i,corrected=true){
    const db=new DatabaseSync(':memory:');try{db.exec('PRAGMA foreign_keys=ON;CREATE TABLE parent(id INTEGER PRIMARY KEY AUTOINCREMENT,label TEXT);CREATE TABLE child(parent_id INTEGER REFERENCES parent(id));');
      for(const id of i.existing)db.prepare('INSERT INTO parent VALUES(?,?)').run(id,'Existing');
      const newId=Number(db.prepare("INSERT INTO parent(label) VALUES('Imported')").run().lastInsertRowid);
      const mapping={source:i.sourceId,target:newId};
      const error=corrected&&i.childSource!==i.sourceId?'unmapped-source-parent':failed(()=>db.prepare('INSERT INTO child VALUES(?)').run(corrected?newId:i.childSource));
      return {mapping,error,parents:rows(db,'SELECT * FROM parent ORDER BY id'),children:rows(db,'SELECT * FROM child')};
    }finally{db.close();}
  };
const cases=[{"label":"misleading base","input":{"existing":[7,100],"sourceId":7,"childSource":7},"expected":{"mapping":{"source":7,"target":101},"error":null,"parents":[{"id":7,"label":"Existing"},{"id":100,"label":"Existing"},{"id":101,"label":"Imported"}],"children":[{"parent_id":7}]},"corrected":false},{"label":"corrected base","input":{"existing":[7,100],"sourceId":7,"childSource":7},"expected":{"mapping":{"source":7,"target":101},"error":null,"parents":[{"id":7,"label":"Existing"},{"id":100,"label":"Existing"},{"id":101,"label":"Imported"}],"children":[{"parent_id":101}]},"corrected":true},{"label":"coincidental source-target number equality","input":{"existing":[],"sourceId":1,"childSource":1},"expected":{"mapping":{"source":1,"target":1},"error":null,"parents":[{"id":1,"label":"Imported"}],"children":[{"parent_id":1}]},"corrected":true},{"label":"unmapped child source","input":{"existing":[7,100],"sourceId":7,"childSource":8},"expected":{"mapping":{"source":7,"target":101},"error":"unmapped-source-parent","parents":[{"id":7,"label":"Existing"},{"id":100,"label":"Existing"},{"id":101,"label":"Imported"}],"children":[]},"corrected":true}];
for(const test of cases){
  const actual=run(test.input,test.corrected);
  assert.deepEqual(actual,test.expected,test.label);
  console.log(test.label,actual);
}
下載可執行例子 ↓

下次載入前核邊界

空目標剛好同號,不能證明直接拷鍵可靠。未知子來源 8 要暫停,不寫子行。

可編輯邊界紀錄及預期輸出
情況輸入 JSON預期輸出 JSON
來源與目標剛好同號{"existing":[],"sourceId":1,"childSource":1}{"mapping":{"source":1,"target":1},"error":null,"parents":[{"id":1,"label":"Imported"}],"children":[{"parent_id":1}]}
子來源沒有對應{"existing":[7,100],"sourceId":7,"childSource":8}{"mapping":{"source":7,"target":101},"error":"unmapped-source-parent","parents":[{"id":7,"label":"Existing"},{"id":100,"label":"Existing"},{"id":101,"label":"Imported"}],"children":[]}
下載可編輯 CSV ↓

原例子行必須指哪個目標父編號?

查看參考答案

101,要依分配對應。目標 7 是不相關舊紀錄,即使存在外鍵檢查會過。

相關資源

系統整合服務 →