No autoincrement for Integer Primary key in sqlite3 - python

In the sqlite3 faq, it is mentioned that an integer primary key being fed a null value would autoincrement. But this is not happening for me.
to replicate, a table in sqlite3, CREATE TABLE dummy( serial_num INTEGER PRIMARY KEY, name TEXT); and fill it using python,
import sqlite3 as lite
con = lite.connect('some.db')
cur=con.cursor()
data = "someone's name"
cur.execute("INSERT INTO dummy VALUES(NULL, ?)", data)
con.commit()
The first attribute serial_num is being shown blank while the name attribute is fine. When I do SELECT serial_num FROM dummy I just get a bunch of blank spaces. What am I doing wrong?

This is one of SQLite's quirks. From the fine manual:
According to the SQL standard, PRIMARY KEY should always imply NOT NULL. Unfortunately, due to a long-standing coding oversight, this is not the case in SQLite. Unless the column is an INTEGER PRIMARY KEY SQLite allows NULL values in a PRIMARY KEY column. We could change SQLite to conform to the standard (and we might do so in the future), but by the time the oversight was discovered, SQLite was in such wide use that we feared breaking legacy code if we fixed the problem.
The documentation on INTEGER PRIMARY KEY is a little unclear about what precisely is required for a column to be this special INTEGER PRIMARY KEY that auto-increments but the reality is that the column needs to be NOT NULL if you want to use the NULL value to mean "give me the next auto-incrementing value" when inserting:
create table dummy (
serial_num integer primary key not null,
name text
);
If you leave out the not null, you need to do your inserts like this:
insert into dummy (name) values (?)
to get the auto-increment value for serial_num. Otherwise, SQLite has no way of telling the difference between a NULL meaning "give me the next auto-increment value" and a NULL meaning "put a NULL value in serial_num because the column allows NULLs".

The insert syntax provided above does not seem to work in the absence of not null.
Here's an example - note that the ID field is not autoincremented even though I use the insert format that you specified above.
sqlite> .schema logTable
CREATE TABLE logTable (ID INTEGER PRIMARY_KEY, ts REAL, level TEXT, message TEXT);
sqlite> INSERT into LOGTABLE (ts, level, message) VALUES (111, "autoinc test", "autoinc test");
sqlite> select * from logtable where ts = 111;
|111.0|autoinc test|autoinc test
sqlite>
It does work with the NOT NULL workaround.
sqlite> create TABLE logTable (ID INTEGER PRIMARY KEY NOT NULL, ts REAL, level TEXT, message TEXT);
sqlite> INSERT into LOGTABLE (ts, level, message) VALUES (222, "autoinc test", "autoinc test");
sqlite> select * from logtable where ts = 222;
1|222.0|autoinc test|autoinc test
I apologize for posting this as a new answer instead of commenting on the previous answer, but my reputation score is too low to add comments, and I thought that it was important to note that the alternate insert statement is not an adequate workaround.

Related

Why when I try create table in SQLite by python it shows me: error near "<": syntax error

I test commands for sql by python. Generaly everything is okey, in this case, its doesn't work.
import sqlite3
conn = sqlite3.connect('Chinook_Sqlite.sqlite')
cursor = conn.cursor()
result = None
try:
cursor.executescript("""CREATE TABLE <New>;""")
result = cursor.fetchall()
except sqlite3.DatabaseError as err:
print("Error: ", err)
else:
conn.commit()
print(result)
conn.close()
Name writes with out <> and must include: name, type, default value after in ().
https://www.sqlite.org/lang_createtable.html - thanks #deceze
The "CREATE TABLE" command is used to create a new table in an SQLite
database. A CREATE TABLE command specifies the following attributes of
the new table:
he name of the new table.
The database in which the new table is created. Tables may be created in the main database, the temp database, or in any attached
database.
The name of each column in the table.
The declared type of each column in the table.
A default value or expression for each column in the table.
A default collation sequence to use with each column.
Optionally, a PRIMARY KEY for the table. Both single column and composite (multiple column) primary keys are supported.
A set of SQL constraints for each table. SQLite supports UNIQUE, NOT NULL, CHECK and FOREIGN KEY constraints.
Optionally, a generated column constraint.
Whether the table is a WITHOUT ROWID table.
cursor.executescript("""CREATE TABLE New ( AuthorId INT IDENTITY (1, 1) NOT NULL, AuthorFirstName NVARCHAR (20) NOT NULL, AuthorLastName NVARCHAR (20) NOT NULL, AuthorAge INT NOT NULL);""")

ID is not autoincrementing in my sqlite3 database

I am trying to build a GUI CRUD app in python where a user enters an object; say an apple the amount of objects (10) and the date at which they conducted their research could be today or yesterday etc (29/03/2021) in this format.
This data then gets sent to a sqlite3 database so reports can be run.
When implementing and the python file the sqlite database contains all the information added bar the ID which should be autoincremented, instead it shows NULL.
import sqlite3
class Database:
def __init__(self, db):
self.conn = sqlite3.connect(db)
self.cur = self.conn.cursor()
self.cur.execute("CREATE TABLE IF NOT EXISTS errors (ID INTEGER PRIMARY KEY AUTOINCREMENT,
Subject text, Total integer, Date text)")
self.conn.commit()
def insert(self, subject, total, date):
self.cur.execute("INSERT INTO errors VALUES (NULL, ?, ?, ?)", (subject, total, date))
self.conn.commit()
So basically my ID column is not incrementing and is saying NULL. I have tried removing "AUTOINCREMENT" aswell as some say it is not necessary with PRIMARY KEY PRESENT but still doesn't work.
Well yes, autoincrement ids only works when sqlite is creating the value. Here you're giving it NULL explicitely so it does as you ask and uses NULL.
If you want the default behaviour, just don't provide a value for that column:
insert into errors (subject, total, date) values (?, ?, ?)
unlike postgres, sqlite apparently doesn't support DEFAULT pseudo-expressions, so that seems to not be an option.
Incidentally, the AUTOINCREMENT is probably why sqlite doesn't error on NULL:
According to the SQL standard, PRIMARY KEY should always imply NOT NULL. Unfortunately, due to a bug in some early versions, this is not the case in SQLite. Unless the column is an INTEGER PRIMARY KEY or the table is a WITHOUT ROWID table or the column is declared NOT NULL, SQLite allows NULL values in a PRIMARY KEY column.
meaning a column which is strictly declared as INTEGER PRIMARY KEY should implicitly reject NULL values, as it will make the column an alias / replacement for the implicit ROWID.

Python ODBC Create Table IF Not Exists

I have a SQL ODBC statement in Python but it always returns an error. On the SQL Server the statement works fine. Can anyone help me here?
execute("IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='tablename' AND xtype='U')
CREATE TABLE tablename (id INTEGER PRIMARY KEY, fieldA NVARCHAR(Max) NOT NULL, fieldB NVARCHAR(Max) NOT NULL)")
EDIT: Please note that you have to use triple quotations for multi-line string in Python, if the execute statement is pasted above as written in your script it most likely fails from the newline.
execute("""IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='tablename' AND xtype='U')
CREATE TABLE tablename (id INTEGER PRIMARY KEY, fieldA NVARCHAR(Max) NOT NULL, fieldB NVARCHAR(Max) NOT NULL)""")
I recommend just using the pandas to_sql function. It will create the table with all necessary columns in case it does not exist.
You can use the if_exists parameter to handle an already existing table ('append'/'replace'/'fail')

insert with select, get inserted value

I am using specific query for generating ids without autoincrement:
'insert into global_ids (`id`) select (((max(id)>>4)+1)<<4)+1 from global_ids'
CREATE TABLE `global_ids` (
`id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
I have problem of fetching last inserted row id (because i am not using autoincrement, cur.lastrowid is None)
How can i fetch result of inserted row id value in my case?
If you don't care about race conditions, you can get the highest id value in the table with SELECT MAX(id) AS id FROM global_ids.
But, you do care about race conditions. What happens if two copies of your python program are running? One may do an insert, then another may do an insert before the first one does the select I suggested. If that happens, the select will return a wrong value.
You could try
START TRANSACTION;
SELECT (((max(id)>>4)+1)<<4)+1 INTO #id FROM global_ids WITH UPDATE;
INSERT INTO global_ids (id) VALUES (#id);
COMMIT;
SELECT #id AS id;
to serialize your id generation / retrieval operation with transactions. (This won't work in MyISAM tables, because they ignore transactions.)
But, your best bet to avoid race condition hassles in MySQL is to actually use the autoincrementing feature. You could try this.
CREATE TABLE global_ids (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
Then issue these queries one after the other:
INSERT INTO global_ids () VALUES ();
SELECT (((LAST_INSERT_ID()>>4)+1)<<4)+1 AS id;
That will use the auto-incrementing feature to generate your transformed id values without running the risk of race conditions.

IntegrityError: ORA-01400: cannot insert NULL into

I'm trying to parse the following parameters into cursor.execute:
sql_insert = 'insert into sms_messaging (result_sms, msgid, msgparts) values (:1, :2, :3)'
smsreport_text_new = ('success', '2D67C865FB6FA25A9261C75E8D2F0F2B ', 1)
cursor.execute(sql_insert, smsreport_text_new)
Afterwards I'm receiving the following error message:
IntegrityError: ORA-01400: cannot insert NULL into ("ADAUTH"."SMS_MESSAGING"."DATE_SMS")
In order to build the table, my .sql file looks like this:
-- Create template
CREATE TABLE sms_messaging(
date_sms DATE primary key,
result_sms VARCHAR(20),
msgid VARCHAR(128),
msgparts INTEGER,
errorcode INTEGER,
errormessage VARCHAR(128)
);
Even though for this particular sql_insert there is no date_sms, the error message indicates an issue with this. I don't see where I'm going wrong. How can I resolve this issue?
The problem is that you have a column DATE_SMS that is declared NOT NULL and has no default value. You need to give it a value. I don't know what it should be, but perhaps something like this:
insert into sms_messaging (result_sms, msgid, msgparts, date_sms)
values (:1, :2, :3, sysdate);
This would put the current date/time into the field.
The problem is that the DATE_SMS column is the primary key, and therefore it doesn't accept a NULL value. Since you haven't provided a value in your insert statement for that column, that's why you're getting the error.
I'm not sure about oracle, but in SQL Server, for example, you can setup a default value for a column, so I would, for example, set 'GETDATE()` as the default for a date column. Maybe Oracle has that too.
You should not have DATE_SMS as primary key! Rather use msg_id:
CREATE TABLE sms_messaging(
date_sms DATE,
result_sms VARCHAR(20),
msgid VARCHAR(128) primary key,
msgparts INTEGER,
errorcode INTEGER,
errormessage VARCHAR(128)
);

Categories