如何阻止主键ID自增?

0 投票
2 回答
69 浏览
提问于 2025-04-12 13:45

简化的核心代码,供参考:

def sqlInput(groupValues, userLogin):
    sqliteConnection = None
    try:
        sqliteConnection = sqlite3.connect('NEA.db')
        cursor = sqliteConnection.cursor()
        print()
        print("Connected to SQLite server.")
        print()
        cursor.execute('''CREATE TABLE IF NOT EXISTS "Food Tracker" (
                        ID INTEGER PRIMARY KEY,
                        "Entry Number" INTEGER,
                        "Calories" INTEGER,
                        "Food Item" TEXT,
                        "Date" TEXT,
                        "Time" TEXT
                    )''')

        insertQuery ="""INSERT INTO Food Tracker(
                                    "ID", "Food Item", "Calories", "Date", Time) 
                                VALUES 
                                (?,?,?,?,?,?)"""
        inserting = list(
                zip(userLogin.primarykey, groupValues.entry, groupValues.food, map(int, groupValues.calories), groupValues.date, groupValues.time))
        cursor.executemany(insertQuery,inserting)
        sqliteConnection.commit()
        print("Successfully entered data into SQLite database.")
        print()
        menu(userLogin)
    except sqlite3.Error as error:
        print()
        print("Failed to insert data into SQLite database:", error)
        print()
    finally:
        if sqliteConnection:
            sqliteConnection.close()

我在这些代码行上遇到了困难:

 insertQuery ="""INSERT INTO Food Tracker(
                                    ID, Food Item, Calories, Date, Time) 
                                VALUES 
                                (?,?,?,?,?)"""
        inserting = list(
                zip(userLogin.primarykey, groupValues.entry, groupValues.food, map(int, groupValues.calories), groupValues.date, groupValues.time))

所有数据都能顺利插入到数据库里,除了ID这一列没有问题。我的ID应该是存储在 userInput.primarykey 里的,但它并没有显示为我的ID。比如说,我希望ID显示为“62381”,这是一个有效的示例ID,但它却显示为“1”,并且每次添加新数据时这个数字都会增加。

我该如何让我的ID保持为62381,而不是每次都增加呢?

我尝试过添加一个单独的外键,也试过改变括号的顺序,还用过其他命令,比如MAP和ZIP。

但都没有效果,AI也没给出答案,我真的需要一个解决方案。

如果需要更多信息,请告诉我,虽然我觉得我已经提供了你需要的所有内容。

请帮我解决这个问题,我之前有很多问题被关闭,但这个问题是有效的,我需要一个快速的解决方案。

错误信息:无法获取最后的条目编号:唯一约束失败:食品追踪器.ID。我该如何多次使用主键呢?

2 个回答

2

看起来你说的是自动递增的主键。这是数据库中一个常见的功能,通常在你不打算为主键设置具体值的情况下使用。

从这行DDL(数据定义语言)中,我们可以看到你隐式地将列 FoodTracker.ID 定义为一个自动递增的主键。

ID INTEGER PRIMARY KEY,

你可以查看这个 帖子,了解这种行为的解释。

从你尝试插入的值来看,你在这个SQL中将第一个 ? 参数传递给了一个特定的值,这表明你试图为这个字段设置一个具体的值。

INSERT INTO Food Tracker(
                                    ID, Food Item, Calories, Date, Time) 
                                VALUES 
                                (?,?,?,?,?)

不过,这样是行不通的。我建议你参考这个 帖子 中被接受的答案,建议在你的表定义中为id应用一个 UNIQUE 约束。

还建议使用 INT 数据类型来替代 INTEGER,这听起来有点奇怪,但@dan04在他对 这个旧帖子中的评论中提到过。

这两种数据类型的区别可以通过类型亲和性的概念来解释,你可以在官方的 文档第3节中阅读。基本上,INTEGER 是一种“灵活”的数据类型,允许与其他相似类型的字段兼容(即动态类型)。然而,自动递增的主键需要表现得像是静态类型,因此使用了类型 INT,而不是亲和性 INTEGER

在第2节中我们可以看到

在SQLite版本3的数据库中,除了INTEGER PRIMARY KEY列外,任何列都可以用来存储任何存储类的值。

这似乎表明,以这种方式定义的列将无法存储你尝试插入的值。

针对你在这个帖子中的评论,看来@Schwern是对的,你的意图是插入一个外键引用,而不是你最初描述的主键值。

在这种情况下,虽然上述内容仍然适用于主键,但你可能需要类似这样的DDL语句:

CREATE TABLE IF NOT EXISTS "UserLogin" (
                        ID INTEGER PRIMARY KEY,
                        "UserName" TEXT
);


CREATE TABLE IF NOT EXISTS "Food Tracker" (
                        ID INTEGER PRIMARY KEY,
                        "UserLoginId" INTEGER,
                        "Entry Number" INTEGER,
                        "Calories" INTEGER,
                        "Food Item" TEXT,
                        "Date" TEXT,
                        "Time" TEXT,
                        FOREIGN KEY (UserLoginId) REFERENCES UserLogin (ID)
);

我在这里用SQL演示底层逻辑,但你可以根据需要在你的Python应用程序中实现适当的参数化。

首先,确保我们有一个用户:

INSERT INTO UserLogin (
    UserName  
)
VALUES ('42123');

然后我们向食物追踪器添加一条记录。

INSERT INTO "Food Tracker" (
    "UserLoginId", 
    "Entry Number"
    --,etc.
) 
SELECT
    (
        SELECT Id FROM UserLogin
        WHERE UserName = '42123'
    ) as UserLoginId,
    7 as [EntryNumber];

注意,在插入时没有指定 Food Tracker 的主键。同时注意子查询,这允许我们通过一个自由定义的字符串来引用特定用户,同时在UserLogin表中保留一个唯一的、自动递增的主键。这通常是有用的,也是推荐的表设计。

最后,我们可以通过简单的查找来查询与特定用户相关的追踪信息:

SELECT
    ul.UserName,
    ft.* 
FROM "Food Tracker" ft
    INNER JOIN UserLogin ul ON ft.UserLoginId = ul.Id
WHERE ul.UserName = '42123';
0

我怎么能多次使用主键呢?

不可以。主键必须是唯一的

我怎么才能让我的ID保持为62381,比如在userInput.primarykey中,而不是自动增加呢?

别这样做。保持ID不变,不要自己插入。integer primary key在SQLite中是特别的,默认情况下会提供一个好的唯一主键。其他数据库可能需要你声明它为自动增加或身份标识。

相反,可以添加另一列来存储userLogin.primarykey

我只能猜测userLogin.primarykey是与该Food Tracker行相关的用户ID。它应该是“用户ID”,并声明为外键,引用你的用户表。

create table "Food Tracker" (
  id integer primary key,

  "User ID" integer not null references Users(id),

  -- I notice you're not using this in your insert.
  -- What is it?
  "Entry Number" integer,
  
  "Calories" integer,
  "Food Item" text not null,

  "Date" text,
  "Time" text
)

这假设你有一个用户表可以参考,并且每个userLogin.primarykey都有一个条目。你可能需要在插入他们的食物追踪信息之前先插入用户。


注意1:你的插入有5个值,但你传入了6个。

注意2:小心使用CREATE TABLE IF NOT EXISTS。如果你改变了表的定义它不会有任何作用; 你将使用旧的定义。对于像这样的测试数据库,建议删除表再重新创建。

注意3:列名中有空格会带来麻烦,空格用来分隔单词。结果就是你必须引用所有内容,这会带来其他问题,比如你的insert可能会忘记加引号。此外,大多数数据库不区分大小写,所以thingThing指的是同一个东西,但"Thing"可能只指Thing而不指thing。考虑使用这种风格这种风格

注意4:将日期和时间存储在一个列中,“Tracked At”。这样占用的存储空间更少,处理数据也会简单得多。在导入数据时,你应该解决这些问题,插入之前将日期和时间合并。同时,习惯使用合适的类型,datetimetimestamp(SQLite对类型的宽松处理是比较特殊的)。

撰写回答