如何阻止主键ID自增?
简化的核心代码,供参考:
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 个回答
看起来你说的是自动递增的主键。这是数据库中一个常见的功能,通常在你不打算为主键设置具体值的情况下使用。
从这行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';
我怎么能多次使用主键呢?
不可以。主键必须是唯一的。
我怎么才能让我的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
可能会忘记加引号。此外,大多数数据库不区分大小写,所以thing
和Thing
指的是同一个东西,但"Thing"
可能只指Thing
而不指thing
。考虑使用这种风格或这种风格。
注意4:将日期和时间存储在一个列中,“Tracked At”。这样占用的存储空间更少,处理数据也会简单得多。在导入数据时,你应该解决这些问题,插入之前将日期和时间合并。同时,习惯使用合适的类型,datetime
或timestamp
(SQLite对类型的宽松处理是比较特殊的)。