SQLite instr 定位分隔符:零、空值和一基位置怎样决定拆分结果

前天 3阅读

一列备注按第一个冒号分成名称和内容,查询先用 instr 找位置,再交给 substr。普通样本没问题,缺少冒号的记录却可能被当成另一种合法拆分。原因是查找结果不仅是一串数字:它还包含“没找到”和“输入缺失”两种状态,必须先判断,再把有效位置用于截取。

先明确结果中的三种含义

instr 返回第一次出现的位置,从一开始计数;完全找不到时返回零,任一参数为 NULL 时返回 NULL。因此位置一表示就在开头,不能当成未找到。它也不会自动找到最后一个分隔符;如果正文自己含冒号,第一次出现之后的全部内容仍应保留下来。

下面保存为 demo.py,运行 python demo.py。六条合成记录覆盖中间、开头、末尾、缺失、空串和空值。查询先为查找结果取名,再分别输出状态和左右两段。只有位置大于零才做拆分,这样调用方可以区分空字段与根本没有成功识别的记录。

import sqlite3

db = sqlite3.connect(":memory:")
try:
    db.execute("CREATE TABLE sample(id INTEGER PRIMARY KEY, value TEXT)")
    db.executemany("INSERT INTO sample VALUES (?, ?)", [
        (1, "甲:乙:丙"), (2, ":首"), (3, "尾:"),
        (4, "没有分隔符"), (5, ""), (6, None),
    ])
    rows = db.execute("""
        WITH located AS (
            SELECT id, value, instr(value, ':') AS p FROM sample
        )
        SELECT id, p,
            CASE WHEN p IS NULL THEN 'null-input'
                 WHEN p = 0 THEN 'missing' ELSE 'found' END,
            CASE WHEN p > 0 THEN substr(value, 1, p - 1) END,
            CASE WHEN p > 0 THEN substr(value, p + 1) END
        FROM located ORDER BY id
    """).fetchall()
    assert rows == [
        (1, 2, "found", "甲", "乙:丙"),
        (2, 1, "found", "", "首"),
        (3, 2, "found", "尾", ""),
        (4, 0, "missing", None, None),
        (5, 0, "missing", None, None),
        (6, None, "null-input", None, None),
    ]
    for row in rows:
        print(row)
    text = "甲:乙"
    blob = text.encode("utf-8")
    assert db.execute("SELECT typeof(?), typeof(?)", (text, blob)).fetchone() == ("text", "blob")
    positions = db.execute("SELECT instr(?, ?), instr(?, ?)",
                           (text, ":", blob, b":")).fetchone()
    assert positions == (2, 4)
    suffixes = db.execute("SELECT substr(?, ?), substr(?, ?)",
                          (text, positions[0] + 1, blob, positions[1] + 1)).fetchone()
    assert suffixes == ("乙", "乙".encode("utf-8"))
    assert db.execute("SELECT instr(?, NULL)", (text,)).fetchone() == (None,)
    print("text/blob positions:", positions)
    print("matching representations: suffixes verified")
finally:
    db.close()

第一条得到左侧“甲”和右侧“乙:丙”;第二条左侧为空,第三条右侧为空,两者都属于 found。没有冒号与空字符串属于 missing,原值为空则属于 null-input。这些状态比一律返回两个空串更有用,后续校验可以准确指出输入缺失还是格式不符。

SQLite instr 定位分隔符:零、空值和一基位置怎样决定拆分结果

AI生成概念示意图:查找先确定目标出现的位置,位置需要和被搜索的文本或字节序列对应。

把一基位置交给对应的截取接口

SQL 中 substr 的正起点同样从一开始,所以左段从一取到分隔符之前,长度写为 p-1;右段从 p+1 开始。末尾冒号之后没有内容,得到空串是合理结果。如果把位置交回使用零基下标的语言,还应在接口边界明确转换,不能在多个层里反复减一。

本例把未命中的左右段留为 NULL,同时保留 status。不要先将位置中的 NULL 替换成零,再期待后面还能够区分两类输入。原始状态一旦合并,报错信息、统计数量以及后续修复队列都可能丢失依据。是否接受空名称和空内容,则由具体格式另行规定。

两个参数都为 BLOB 才按字节搜索

代码后半段把同一份文字明确编码成 UTF-8,再将 bytes 绑定给查询。文本里的冒号位于二,二进制序列里的冒号位于四。关键是查找与后续截取使用同一种表示:文本结果交给文本 substr,BLOB 位置交给 BLOB substr,不要把其中一个位置套到另一份数据。

这里直接绑定编码后的 bytes,使测试不依赖数据库文本转 BLOB 时采用的编码。检查类型的断言也提醒我们,参数的实际存储类别会影响函数行为。不要仅凭列名带有 text 或某个值看起来像文字,就认为两个实参必然都按预期方式参与搜索。

对于业务格式,分隔符还应是明确的非空值。空查找串虽然可以被函数接受,却不表示发现了一个实际字段边界,通常应该在查询入口拒绝。若分隔符包含多个字符,右段起点也不能继续机械地只加一,应按同一表示下的分隔符长度移动。

这套写法适合只按第一个固定分隔符拆分的简单字段。带引号、转义或嵌套结构的数据需要自己的解析规则,连续调用 instr 无法自动理解这些结构。保留六类输入及文本、二进制两个断言,就能在改查询时检查拆分契约是否仍然成立。

资料核对日期:2026年10月2日(北京时间)。示例在 Python 3.12.14、SQLite 3.53.1 中独立运行。

官方参考:SQLite instr 与 substr 官方说明;Python sqlite3 类型映射。

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。