如何将 PROGRAMS 字段中的代码和金额拆分为多条记录?
数据阿明
可以。将 PROGRAMS 按分号拆分,每两个元素组成一条记录:第一个是 PROGRAM_CD,第二个是 PROGRAM_AMT,并复制原行的 ACCOUNTID。
如果数据通过 CSV 导入,可以先用脚本生成明细文件,再导入目标表。
拆分时必须保留原始顺序
例如:
01;25000;02;40411
按位置配对后为:
01 → 25000
02 → 40411
不能先对代码或金额排序,也不能分别拆分后随意关联,否则可能导致代码与金额错配。
目标表中的每一行表示某个账户的一项 PROGRAM。账户与明细仍是一对多关系,因此 ACCOUNTID 会重复出现。
用 Python 生成可导入的明细文件
下面的脚本读取 accounts.csv,生成 account_programs.csv。示例文件使用 | 分隔列,因此脚本使用 delimiter="|";如果实际输入文件使用逗号,调整输入文件对应的分隔符即可。
import csv
from decimal import Decimal, InvalidOperation
records = []
with open("accounts.csv", "r", encoding="utf-8-sig", newline="") as source:
reader = csv.DictReader(source, delimiter="|")
required_columns = {"ACCOUNTID", "PROGRAMS"}
if not required_columns.issubset(reader.fieldnames or []):
raise ValueError("Missing ACCOUNTID or PROGRAMS column")
for row_number, row in enumerate(reader, start=2):
account_id = (row["ACCOUNTID"] or "").strip()
programs = row["PROGRAMS"]
if None in row or not account_id or programs is None:
raise ValueError(f"Invalid source row: {row_number}")
tokens = [part.strip() for part in programs.split(";")]
if len(tokens) % 2 != 0 or any(token == "" for token in tokens):
raise ValueError(
f"Invalid code/amount pairs at row {row_number}: {account_id}"
)
for index in range(0, len(tokens), 2):
program_code = tokens[index]
amount_text = tokens[index + 1]
try:
amount = Decimal(amount_text)
except InvalidOperation as exc:
raise ValueError(
f"Invalid amount at row {row_number}: {amount_text}"
) from exc
if not amount.is_finite():
raise ValueError(
f"Non-finite amount at row {row_number}: {amount_text}"
)
records.append((account_id, program_code, format(amount, "f")))
with open("account_programs.csv", "w", encoding="utf-8", newline="") as target:
writer = csv.writer(target, delimiter="|")
writer.writerow(["ACCOUNTID", "PROGRAM_CD", "PROGRAM_AMT"])
writer.writerows(records)
脚本会先解析全部记录,再创建输出文件。如果发现缺少金额、空代码或无效金额,就会报错并停止。脚本不会删除空元素,因为这样可能改变后续代码和金额的配对位置。
题目中的输入按上述规则展开后应得到:
ACCOUNTID|PROGRAM_CD|PROGRAM_AMT
AC30400290|01|25000
AC30400290|02|40411
AC30400290|04|30730
AC30400290|17|30730
AC01410304|02|65100
AC01410304|22|48401
将明细导入 SQL Server
如果目标数据库是 SQL Server,可以先创建明细表,再通过现有的文件导入流程加载生成的文件:
CREATE TABLE dbo.AccountPrograms
(
ACCOUNTID varchar(50) NOT NULL,
PROGRAM_CD varchar(20) NOT NULL,
PROGRAM_AMT decimal(19,2) NOT NULL
);
字符串长度和 decimal(19,2) 都是示例选择,需要根据实际数据调整。导入时,将列分隔符设置为 |,并将第一行识别为列名。
PROGRAM_CD 应使用字符串类型,以保留 01、02 这样的前导零。金额应根据业务需要选择精确数值类型,并在导入前确认数值范围和小数位数符合目标列要求。
需要确认的数据规则
- 空 PROGRAMS 如何处理:示例脚本将其视为错误。如果业务允许账户没有 PROGRAM,可以改为跳过该账户的空值。
- 末尾分号是否合法:
01;25000;会产生一个空元素,脚本会报错。只有确定末尾分号是允许的格式时,才应专门处理。 - 同一账户能否重复出现同一代码:题目没有说明,因此示例表没有添加
(ACCOUNTID, PROGRAM_CD)唯一约束。确认业务规则后再决定是否添加。 - 重复导入如何处理:再次追加同一个文件会产生重复明细,需要在导入流程中明确是替换、更新还是追加。
示例脚本将结果暂存在内存中,适合能够装入内存的文件。数据量较大时,可以逐行写入临时文件,校验完成后再导入数据库。
备注:内容仅供参考。
