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.
Related
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);""")
**What I am trying to do**
I have been trying to insert a record to a table via tkinter, and I want to see the inserted record on mysql.
Issue
After entering a record to tkinter, I received the following error. I looked into the solutions for the same errors posted online, but it was to use auto_increment for column Code (the one with primary key). However, I cannot use auto_increment since the values of the column Code does not always increase. I have also tried deleting primary key for the column but I still got the same error. Any insights on this?
**Error:**
*mysql.connector.errors.IntegrityError: 1062 (23000): Duplicate entry '0' for key 'PRIMARY'*
**Code to create the table:**
#table
cursor=mycon.cursor(buffered=True)
cursor.execute("use iv")
cursor.execute("drop table if exists salesperson")
create_table='''create table salesperson
(
code int(4) primary key,
name varchar(15),
salary int,
itcode char
)'''
cursor.execute(create_table)
**Code to insert data to mysql from tkinter/python:**
from tkinter import *
from tkinter.messagebox import showinfo
def add_data():
code=tcode.get('1.0',END) #retrieve input
name=tname.get('1.0',END)
salary=tsal.get('1.0',END)
itcode=titcode.get('1.0',END)
#DATABASE CONNECTION
if code=="" or name=="" or salary=="" or itcode=="":
messagbox.showinfo("Please fill all the fields")
else:
import mysql.connector as sqltor
connection=sqltor.connect(host="localhost",user="root",password=" ",database="iv")
tkcursor=connection.cursor()
tkcursor.execute("Insert into salesperson values (code,'name',salary,'itcode')")
connection.commit()
messagebox.showinfo("Records inserted")
tkcursor.close()
The problem is in the INSERT statement:
tkcursor.execute("Insert into salesperson values (code,'name',salary,'itcode')")
When you reference an identifier in an SQL statement like this, it's an SQL identifier, not a Python variable. It's not an error in this case because your table coincidentally has columns named code and salary.
But what is the value of these columns? Since this is an INSERT statement, by definition the row doesn't exist yet as the VALUES() clause is evaluated. So the value of all columns of that row is NULL. It's exactly as if you had done this:
tkcursor.execute("Insert into salesperson values (NULL,'name',NULL,'itcode')")
Since code is the primary key, it must be NOT NULL. Even though the column does not have a DEFAULT defined, MySQL has a concept of a "default default." That is, in absence of an explicitly defined default value, each data type will be converted to an appropriate implicit default value if NULL is not accepted (see https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html for documentation on this). For an integer, the implicit default is 0. So your statement works as if you did this:
tkcursor.execute("Insert into salesperson values (0,'name',NULL,'itcode')")
How to fix this? You should use parameters to help you get the values of your Python variables into your SQL INSERT statement. That way the Python variable code will be used, not the SQL column that is also named code. The comment above from nbk hinted at this.
tkcursor=connection.cursor(prepared=True)
tkcursor.execute("Insert into salesperson values (%s,'name',%s,'itcode')", (code, salary,)
connection.commit()
See https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursorprepared.html for more information.
So I am trying to add a new entry into my mySQL Database.
The problem here is, that it increases the id, but does add the entry.
After a little bit of googling I found that a failed INSERT query also increases the AUTO_INCREMENTd value (id in my case).
The mySQL Table is created using
CREATE TABLE IF NOT EXISTS TS3_STAMM_1 (id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(64) NOT NULL, ts3_uid VARCHAR(64) NOT NULL, points INT(8) UNSIGNED NOT NULL); which is called by the function qServer.execute(querystring) inside python's MySQLdb module.
Then I use qString = "INSERT INTO TS3_STAMM_1 (name, ts3_uid, points) VALUES ('{}', '{}', {})".format(name, uid, pnts) (the datatypes are correct, I at least quadrouplechecked) with the function qServer.exectue(qString) to insert a new entry into the database.
But it is incrementing the ID, but its not adding an entry. So my guess would be its a failed query, but why? How does it happen? How to fix it?
Simple SELECT querys work fine the same way, also adding data manually works fine. Only the python query fails.
Note: qServer is the connection to the server, and its defined with:
try:
qConn = MySQLdb.connect(host="...", user="...", passwd="...", db="...")
qServer = qConn.cursor()
except OperationalError:
print("Cannot connect to mySQL Database! Aborting...")
exit(1)
Use commit Luke.
>>> cursor.execute("INSERT INTO employees (first_name) VALUES (%s)", ('Jane', ))
>>> qConn.commit()
Using str.format for creating SQL query is bad idea.
I created a table by following query:
with sqlite3.connect('example.db', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
cur = conn.cursor()
cur.execute("CREATE TABLE Surveys(Id INTEGER PRIMARY KEY, Name TEXT, Desc TEXT, DictObject BLOB, Hash TEXT)")
conn.commit()
Now I have to add some Survey data to Surveys table for every request. Surveys table has Id as primary integer value. This has to be increased upon every insertion - And What is the proper way to do it? Do I have to fetch every row and check what the lastIdis upon every request?
sqlite will automatically provide an id for a INTEGER PRIMARY KEY column on INSERT on a table if you do not provide a value yourself. Just insert data for every column except for the id column.
with sqlite3.connect('example.db', detect_types=sqlite3.PARSE_DECLTYPES) as conn:
cur = conn.cursor()
cur.execute("INSERT INTO Surveys(Name, Desc, DictObject, Hash) VALUES (?, ?, ?, ?",
('somename', 'some description\nof sorts\n',
"{'dump': 'of a dictionary'}", '0xhash'))
You may want to add the keyword AUTOINCREMENT to your id column though; the default is to pick the highest existing row id plus 1, and if you delete data from the table on a regular basis that can lead to reuse of ids (delete the current highest id and it'll be re-used next time round). AUTOINCREMENT guarantees that each generated number will only be used once for a table, independent of deletes.
Note that when you use a sqlite3 connection as a context manager (using the with statement), the commit() is automatically applied for you. Quoting the documentation:
Connection objects can be used as context managers that automatically commit or rollback transactions. In the event of an exception, the transaction is rolled back; otherwise, the transaction is committed.
In other words, you can safely remove the conn.commit() line in your code, it is entirely redundant.
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.