当我将 csv 文件导入到 sqlite 数据库时,它将数字作为字符串导入到整数列中,我该如何解决这个问题?我的 csv 文件中的一行如下所示:
31,c,BB ROSE - 031,c31,,9,7,0,"142,000",0
CSV 文件没有数据类型;一切都是字符串。
要将列中的所有值转换为数字,请使用如下内容:
UPDATE MyTable SET MyColumn = CAST(MyColumn AS INTEGER)
导入 csv 文件时,SQLite 假定所有字段都是文本字段。因此,您需要执行一些额外的步骤才能设置正确的数据类型。
但是,据我了解,您不能使用
ALTER TABLE
语句来修改 SQLite 中的列。 相反,您需要重命名表,创建新表,然后将数据复制到新表中。
https://www.techonthenet.com/sqlite/tables/alter_table.php
所以假设我有一个 employees.csv 文件,我想将其导入到具有正确数据类型的 SQLite 数据库中。
employee_id,last_name,first_name,hire_date
1001,adams,john,2010-12-12
1234,griffin,meg,2000-01-01
2233,simpson,bart,1990-02-23
首先,创建一个名为
mydb.sqlite
的 SQLite 数据库,并将 employees.csv
导入到名为 employees
的 SQLite 表中。
# create sqlite database called mydb.sqlite
# import data from 'employees.csv' into a SQLite table called 'employees'
# unfortunately, sqlite assumes all fields are text fields
$ sqlite3 mydb.sqlite
sqlite> .mode csv
sqlite> .import employees.csv employees
sqlite> .quit
此时,数据已以文本形式导入。首先,我们从数据库中获取
employees
模式并将其保存到 employees.sql
。我们可以使用它来创建一个新脚本,该脚本将重命名表、创建新表并将数据复制到新表中。
$ sqlite3 mydb.sqlite
sqlite> .once employees.sql
sqlite> .schema employees
sqlite> .quit
您现在应该拥有具有以下架构的 employees.sql:
CREATE TABLE employees(
"employee_id" TEXT,
"last_name" TEXT,
"first_name" TEXT,
"hire_date" TEXT
);
现在让我们创建一个名为 alterTable.sql 的 SQL 文件,它将重命名该表,创建一个新表,并将数据复制到新表中。
alterTable.sql
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE employees RENAME TO _employees_old;
CREATE TABLE employees
( "employee_id" INTEGER,
"last_name" TEXT,
"first_name" TEXT,
"hire_date" NUMERIC
);
INSERT INTO employees ("employee_id", "last_name", "first_name", "hire_date")
SELECT "employee_id", "last_name", "first_name", "hire_date"
FROM _employees_old;
COMMIT;
PRAGMA foreign_keys=on;
最后,我们可以在
alterTable.sql
中执行SQL并删除旧的重命名表
$ sqlite3 mydb.sqlite
sqlite> .read alterTable.sql
sqlite> drop table _employees_old;
此时,导入的员工数据应该具有正确的数据类型,而不是默认的文本字段。
如果您这样做,则不必担心 csv 文件中的标题被导入为数据。其他方法可能需要您在导入 csv 文件之前或之后删除标题。
您只需要先使用正确的类型创建表,然后 CSV 导入将保留此类型,因为该表已经存在。
这里是一个示例:
create table table1(name TEXT, wert INT);
.mode csv
.separator ";"
.import "d:/temp/test.csv" table1
如果您需要删除导入的标题行,请在导入后使用类似的内容:
delete from table1 where rowid=1;
或者在您已经对同一个表进行了多次导入的情况下使用此方法:
delete from [table1] where "name"='name'; -- try to use a name of an INT-column for this.
最后你可以像这样检查正确的导入:
.header ON
select * from table1 order by wert;
在 SQLite 中,您无法更改列的类型关联性。因此,您应该
create
您的表格,然后 .import
将您的 CSV 文件放入表格中。如果您的 CSV 文件有标题,则导入时将被视为数据。您可以在导入之前删除标题(在 CSV 文件中),也可以在导入后删除标题(在表格中)。由于 typeof
所有标题字段都将是 TEXT
,因此您可以轻松地在表中找到此标题,其中某些列具有数字类型亲和性。
我使用的是sqlite 3.39.4,我会这样做:
按照上面的建议,创建一个具有正确类型的新表“newtable”,然后从“mycsvtable.csv”导入数据,输入
.mode csv
.import --skip 1 mycsvtable.csv newtable
如果 csv 中有标题,--skip 1 会避免第一行
对
.import --csv
与预先存在的 integer
列 进行一些测试
此技术在以下位置提到: https://stackoverflow.com/a/56816476/895245但我想进行一些快速的边缘案例测试,以更好地了解什么有效,什么无效:
tmp.csv
1
01
10
abc
1.1
0xa
然后导入:
sqlite3 tmp.sqlite 'create table t(x integer)'
sqlite3 tmp.sqlite '.import --csv tmp.csv t'
sqlite3 tmp.sqlite '.mode line' 'select x, x+1, x*2, x=0, x=1 from t'
输出:
x = 1
x+1 = 2
x*2 = 2
x=0 = 0
x=1 = 1
x = 1
x+1 = 2
x*2 = 2
x=0 = 0
x=1 = 1
x = 10
x+1 = 11
x*2 = 20
x=0 = 0
x=1 = 0
x = abc
x+1 = 1
x*2 = 0
x=0 = 0
x=1 = 0
x = 1.1
x+1 = 2.1
x*2 = 2.2
x=0 = 0
x=1 = 0
x = 0xa
x+1 = 1
x*2 = 0
x=0 = 0
x=1 = 0
因此,据我所知,
1
和01
似乎都是正确的数字1
。
十六进制
0xa
不起作用。
其他任何东西都会变成我现在不完全理解或不关心的随机二进制噪声。
在 SQLite 3.40.1、Ubuntu 23.04 上测试。
用Python来做
如果您需要稍微灵活一点的东西,也许这将是最好的方法。最大的问题是这是否会比大表上的其他方法慢,但我现在懒得进行基准测试。
我发现我可以接受 TEXT 值,因为要让它们按数字排序所需要做的就是使用“ORDER BY longitude + 0”。瞧,无论负数还是正数,我的经度排序都是正确的。
是的,“+ 0”转换并不适合所有人。但如果您想做的只是快速正确的 ORDER BY,也许这就是您所需要的!