SQLite 中如果不存在则更改表添加列
我们最近需要向一些现有的 SQLite 数据库表添加列。这可以通过 ALTER TABLE ADD COLUMN
来完成。当然,如果表已经被更改,我们不想理会它。不幸的是,SQLite 不支持 ALTER TABLE
上的 IF NOT EXISTS
子句。
我们当前的解决方法是执行 ALTER TABLE 语句并忽略任何“重复列名”错误,就像 此 Python 示例(但使用 C++)。
但是,我们设置数据库模式的常用方法是使用包含 CREATE TABLE IF NOT EXISTS
和 CREATE INDEX IF NOT EXISTS
语句的 .sql 脚本,可以使用以下命令执行sqlite3_exec
或 sqlite3
命令行工具。我们不能将 ALTER TABLE 放入这些脚本文件中,因为如果该语句失败,则该语句后面的任何内容都不会被执行。
我希望将表定义放在一处,而不是在 .sql 和 .cpp 文件之间拆分。有没有办法在纯 SQLite SQL 中编写 ALTER TABLE ADD COLUMN IF NOT EXISTS 的解决方法?
We've recently had the need to add columns to a few of our existing SQLite database tables. This can be done with ALTER TABLE ADD COLUMN
. Of course, if the table has already been altered, we want to leave it alone. Unfortunately, SQLite doesn't support an IF NOT EXISTS
clause on ALTER TABLE
.
Our current workaround is to execute the ALTER TABLE statement and ignore any "duplicate column name" errors, just like this Python example (but in C++).
However, our usual approach to setting up database schemas is to have a .sql script containing CREATE TABLE IF NOT EXISTS
and CREATE INDEX IF NOT EXISTS
statements, which can be executed using sqlite3_exec
or the sqlite3
command-line tool. We can't put ALTER TABLE
in these script files because if that statement fails, anything after it won't be executed.
I want to have the table definitions in one place and not split between .sql and .cpp files. Is there a way to write a workaround to ALTER TABLE ADD COLUMN IF NOT EXISTS
in pure SQLite SQL?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
我有一个99%纯SQL方法。这个想法是对你的模式进行版本控制。您可以通过两种方式执行此操作:
使用 'user_version' pragma 命令 (
PRAGMA user_version
)来存储数据库架构版本的增量数字。将您的版本号存储在您自己定义的表中。
这样,当软件启动时,它可以检查数据库架构,并在需要时运行
ALTER TABLE
查询,然后增加存储的版本。这比“盲目”尝试各种更新要好得多,特别是如果您的数据库多年来增长和更改了几次。I have a 99% pure SQL method. The idea is to version your schema. You can do this in two ways:
Use the 'user_version' pragma command (
PRAGMA user_version
) to store an incremental number for your database schema version.Store your version number in your own defined table.
In this way, when the software is started, it can check the database schema and, if needed, run your
ALTER TABLE
query, then increment the stored version. This is by far better than attempting various updates "blind", especially if your database grows and changes a few times over the years.如果您在数据库升级语句中执行此操作,也许最简单的方法是捕获在尝试添加可能已存在的字段时引发的异常。
If you are doing this in a DB upgrade statement, perhaps the simplest way is to just catch the exception thrown if you are attempting to add a field that may already exist.
SQLite 还支持名为“table_info”的 pragma 语句,该语句返回表中每列一行以及该列的名称(以及有关该列的其他信息)。您可以在查询中使用它来检查缺少的列,如果不存在则更改表。
示例输出:
http://www.sqlite.org/pragma.html#pragma_table_info
SQLite also supports a pragma statement called "table_info" which returns one row per column in a table with the name of the column (and other information about the column). You could use this in a query to check for the missing column, and if not present alter the table.
Sample output:
http://www.sqlite.org/pragma.html#pragma_table_info
一种解决方法是仅创建列并捕获该列已存在时出现的异常/错误。添加多列时,请将它们添加到单独的 ALTER TABLE 语句中,这样重复的列就不会妨碍创建其他列。
使用 sqlite-net,我们做了类似的事情。它并不完美,因为我们无法区分重复的 SQLite 错误和其他 SQLite 错误。
One workaround is to just create the columns and catch the exception/error that arise if the column already exist. When adding multiple columns, add them in separate ALTER TABLE statements so that one duplicate does not prevent the others from being created.
With sqlite-net, we did something like this. It's not perfect, since we can't distinguish duplicate sqlite errors from other sqlite errors.
对于那些想要将 pragma table_info() 的结果用作更大 SQL 的一部分的人。
关键部分是使用
pragma_table_info('')
而不是pragma table_info('')
。这个答案的灵感来自@Robert Hawkey 的回复。我将其发布为新答案的原因是我没有足够的声誉将其发布为评论。
For those want to use
pragma table_info()
's result as part of a larger SQL.The key part is to use
pragma_table_info('<table_name>')
instead ofpragma table_info('<table_name>')
.This answer is inspired by @Robert Hawkey 's reply. The reason I post it as a new answer is I don't have enough reputation to post it as comment.
PRAGMA的一个方法是table_info(table_name),它返回表的所有信息。
这是如何使用它来检查列是否存在的实现,
您也可以在不使用循环的情况下使用此查询,
threre is a method of PRAGMA is table_info(table_name), it returns all the information of table.
Here is implementation how to use it for check column exists or not,
You can also use this query without using loop,
如果您在 flex/adobe air 中遇到此问题并首先发现自己在这里,我已经找到了解决方案,并将其发布在相关问题上:如果不存在,则将列添加到 sqlite db - flex/air sqlite?
我的评论:https://stackoverflow.com/a/24928437/2678219
In case you're having this problem in flex/adobe air and find yourself here first, i've found a solution, and have posted it on a related question: ADD COLUMN to sqlite db IF NOT EXISTS - flex/air sqlite?
My comment here: https://stackoverflow.com/a/24928437/2678219
您也可以将 CASE-WHEN TSQL 语句与 pragma_table_info 结合使用来了解列是否存在:
You can alternatively use the CASE-WHEN TSQL statement in combination with pragma_table_info to know if a column exists:
逻辑:sqlite_master中的sql列包含表定义,因此它肯定包含带有列名的字符串。
当您搜索子字符串时,它有其明显的局限性。因此,我建议在 ColumnName 中使用更具限制性的子字符串,例如类似这样的内容(需要进行测试,因为“`”字符并不总是存在):
Logic: sql column in sqlite_master contains table definition, so it certainly contains string with column name.
As you are searching for a sub-string, it has its obvious limitations. So I would suggest to use even more restrictive sub-string in ColumnName, for example something like this (subject to testing as '`' character is not always there):
我在 C#/.Net 中获取了上面的答案,并为 Qt/C++ 重写了它,没有太大改变,但我想把它留在这里,供将来寻找 C++'ish' 答案的任何人使用。
I took the answer above in C#/.Net, and rewrote it for Qt/C++, not to much changed, but I wanted to leave it here for anyone in the future looking for a C++'ish' answer.
这是我的解决方案,但是在 python 中(我尝试但未能找到任何与 python 相关的主题的帖子):
我使用 PRAGMA 来获取表信息。它返回一个多维数组,其中包含有关列的信息 - 每列一个数组。我计算数组的数量来获取列的数量。如果没有足够的列,则我使用 ALTER TABLE 命令添加列。
Here is my solution, but in python (I tried and failed to find any post on the topic related to python):
I used PRAGMA to get the table information. It returns a multidimensional array full of information about columns - one array per column. I count the number of arrays to get the number of columns. If there are not enough columns, then I add the columns using the ALTER TABLE command.
如果您一次执行一行,所有这些答案都很好。然而,最初的问题是输入一个将由单个数据库执行执行的 sql 脚本,并且所有解决方案(例如提前检查列是否存在)都需要执行程序了解哪些表和正在更改/添加列或对输入脚本进行预处理和解析以确定此信息。通常,您不会实时或经常运行它。所以捕获异常然后继续前进的想法是可以接受的。问题就在这里……如何继续前进。幸运的是,错误消息为我们提供了执行此操作所需的所有信息。我们的想法是,如果alter table调用出现异常,则执行sql,我们可以在sql中找到alter table行并返回其余行并执行,直到成功或找不到更多匹配的alter table行。下面是一些示例代码,其中我们在数组中包含 sql 脚本。我们迭代执行每个脚本的数组。我们调用它两次以使 alter table 命令失败,但程序成功,因为我们从 sql 中删除了 alter table 命令并重新执行更新的代码。
预期产出
All these answers are fine if you execute one line at a time. However, the original question was to input a sql script that would be executed by a single db execute and all the solutions ( like checking to see if the column is there ahead of time ) would require the executing program either have knowledge of what tables and columns are being altered/added or do pre-processing and parsing of the input script to determine this information. Typically you are not going to run this in realtime or often. So the idea of catching an exception is acceptable and then moving on. Therein lies the problem...how to move on. Luckily the error message gives us all the information we need to do this. The idea is to execute the sql if it exceptions on an alter table call we can find the alter table line in the sql and return the remaining lines and execute until it either succeeds or no more matching alter table lines can be found. Heres some example code where we have sql scripts in an array. We iterate the array executing each script. We call it twice to get the alter table command to fail but the program succeeds because we remove the alter table command from the sql and re-execute the updated code.
expected output
如果“your_column”尚未在表中的列中,则使用简单的 python 代码将“your_column”添加到“your_table”。如果您在 for 循环中找到“your_column”,您也可以中断,对于我这个问题的特殊情况,我不能这样做。您可能还想通过在 col[1] 和“your_column”上使用 .upper() 或 .lower() 来强制比较区分大小写。如果您要添加多于一列,那么我建议更改 for 循环以从您要添加的列表中删除这些列。
Simple python code to add "your_column" to "your_table" if "your_column" is not already in the columns in the table. You can also just break if you find "your_column" in the for loop, for my particular case with this issue I cannot do this. You also might want to enforce case-sensitivity for comparisons by using .upper() or .lower() on col[1] and "your_column". If you are adding more than one column then I reccomend changing the for loop to delete the columns out of the list that you are adding.
我想出了这个查询,
I come up with this query
我用两个查询解决了它。这是我使用 System.Data.SQLite 的 Unity3D 脚本。
I solve it in 2 queries. This is my Unity3D script using System.Data.SQLite.
显然...在 SQLite 中...如果列已经存在,“alter table”语句不会生成异常。
在支持论坛中找到这篇帖子并进行了测试。
Apparently... in SQLite... the "alter table" statement does not generate exceptions if the column already exists.
Found this post in the support forumn and tested it.