import sqlite3
# Constructing the database
def create_database():
#Create a database or connect to a database
conn = sqlite3.connect('games_database.db')
conn.execute("PRAGMA foreign_keys = 1") #Turns ON foreign key constraints
#Create cursor
c = conn.cursor()
#Create tables
#Create game_publisher table1
c.execute("""CREATE TABLE IF NOT EXISTS game_publisher(
gp_id INTEGER NOT NULL,
gp_name TEXT NOT NULL,
gp_year_established INTEGER NOT NULL,
PRIMARY KEY(gp_id))
""")
#Create game_genre table2
c.execute("""CREATE TABLE IF NOT EXISTS game_genre(
genre_id INTEGER NOT NULL,
genre_name TEXT NOT NULL,
PRIMARY KEY(genre_id))
""")
#Create platform table3 (Original Platform)
c.execute("""CREATE TABLE IF NOT EXISTS platform(
platform_id INTEGER NOT NULL,
platform_name TEXT NOT NULL,
PRIMARY KEY(platform_id))
""")
#Create games table4
c.execute("""CREATE TABLE IF NOT EXISTS games(
g_id INTEGER NOT NULL,
g_name TEXT NOT NULL,
g_genre INTEGER NOT NULL DEFAULT 0,
g_pub INTEGER NOT NULL DEFAULT 0,
year_released INTEGER NOT NULL,
original_platform INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY(g_id),
FOREIGN KEY(g_genre) REFERENCES game_genre(genre_id) ON DELETE SET DEFAULT ON UPDATE CASCADE,
FOREIGN KEY(g_pub) REFERENCES game_publisher(gp_id) ON DELETE SET DEFAULT ON UPDATE CASCADE,
FOREIGN KEY(original_platform) REFERENCES platform(platform_id) ON DELETE SET DEFAULT ON UPDATE CASCADE)
""")
#Commit changes
conn.commit()
#Close connection
conn.close()
I've tried looking up online and found that SQLite by default has the Foreign key constraint set to OFF.
I found that I had to use this "conn.execute("PRAGMA foreign_keys = 1")" to turn it ON.
When I try to delete a game_publisher, and then view the games table, I don't see the games table update the game_publisher ID to 0.
Do I have to turn ON the foreign key constraint every time I make a connection to the database?
The problem is this:
ON DELETE SET DEFAULT
with the definition of the foreign keys in games, because the default value for all of them is defined to be 0 which (I suspect) does not correspond to a valid id of the corresponding referenced tables.
What you can do is define the foreign keys as nullable and with ON DELETE SET NULL like this:
CREATE TABLE IF NOT EXISTS games (
g_id INTEGER NOT NULL,
g_name TEXT NOT NULL,
g_genre INTEGER, -- nullable
g_pub INTEGER, -- nullable
year_released INTEGER NOT NULL,
original_platform INTEGER, -- nullable
PRIMARY KEY(g_id),
FOREIGN KEY(g_genre) REFERENCES game_genre(genre_id) ON DELETE SET NULL ON UPDATE CASCADE,
FOREIGN KEY(g_pub) REFERENCES game_publisher(gp_id) ON DELETE SET NULL ON UPDATE CASCADE,
FOREIGN KEY(original_platform) REFERENCES platform(platform_id) ON DELETE SET NULL ON UPDATE CASCADE
)
Now when you delete a row from a parent table, the child key value will be set to NULL, which is allowed.
Finally, for the question:
Do I have to turn ON the foreign key constraint every time I make a
connection to the database?
the answer is yes, if you are going to use operations involving the integerity of the foreign keys, like the insertion of a new row or update a row in the child table games or the modification of rows (deletions or updates) of the parent tables.
Related
I'm currently tryign to learn how to use SQLite3 and am trying to seperate setting up the DB through functions. My second function is where I'm having the error : AttributeError: 'NoneType' object has no attribute 'cursor'. After looking at the SQLite3 doucmentation, I got the impression that it's best to seperate diffrent methods for the DB into diffrent functions.
import sqlite3
from sqlite3 import Error
def create_connection(CRM):
"""
This Function will check to see if the Database is already created, if not will be created.
:param CRM: Location of the Database
:return: None
"""
db = None
try:
db = sqlite3.connect(CRM)
print(sqlite3.version)
except Error as e:
print(e)
finally:
if db:
db.close()
return db
def create_table(db):
cur = db.cursor()
G8Personnel = ''' CREATE TABLE [IF NOT EXISTS] G8Personnel(
DoD INTEGER NOT NULL PRIMARY KEY,
Rank varchar NOT NULL,
FirstName varchar NOT NULL,
LastName varchar NOT NULL,
Role varchar NOT NULL
)'''
cur.execute(G8Personnel)
Company = '''CREATE TABLE [IF NOT EXISTS] Company(
CompanyName varchar PRIMARY KEY,
)'''
cur.execute(Company)
Employee = '''CREATE TABLE [IF NOT EXISTS] Employee(
AutoID INTEGER PRIMARY KEY AUTOINCREMENT,
FirstName varchar NOT NULL,
LastName varchar NOT NULL,
Email varchar NOT NULL,
JobTitle varchar,
WebPage varchar,
Notes varchar,
Company varchar
FOREIGN KEY (Company) REFERENCES Company(CompanyName)
)'''
cur.execute(Employee)
Meeting = '''CREATE TABLE [IF NOT EXISTS] Meeting(
AutoID INTEGER PRIMARY KEY AUTOINCREMENT,
Date1 real NOT NULL,
DoD INTEGER NOT NULL,
Employee INTEGER NOT NULL,
MeetingNotes varchar NOT NULL
FOREIGN KEY (DoD) REFERENCES G8Personnel (DoD)
FOREIGN KEY (Employee) REFERENCES Employee (AutoID)
)'''
cur.execute(Meeting)
if __name__ == '__main__':
db = None
create_connection(r'C:\Users\c94re\Documents\Git-Repo\CRM\CRM.db')
create_table(db)
You are not capturing the return value of create_connection. Try to assign the return type to a variable and try again.
if __name__ == '__main__':
db = create_connection(r'C:\Users\c94re\Documents\Git-Repo\CRM\CRM.db')
create_table(db)
I think this should do it, if you have other problems, please edit your question.
I have a function that I use to update tables in PostgreSQL. It works great to avoid duplicate insertions by creating a temp table and dropping it upon completion. However, I have a few tables with serial ids and I have to pass the serial id in a column. Otherwise, I get an error that the keys are missing. How can I insert values in those tables and have the serial key get assigned automatically? I would prefer to modify the function below if possible.
def export_to_sql(df, table_name):
from sqlalchemy import create_engine
engine = create_engine(f'postgresql://{user}:{password}#{host}:5432/{user}')
df.to_sql(con=engine,
name='temporary_table',
if_exists='append',
index=False,
method = 'multi')
with engine.begin() as cnx:
insert_sql = f'INSERT INTO {table_name} (SELECT * FROM temporary_table) ON CONFLICT DO NOTHING; DROP TABLE temporary_table'
cnx.execute(insert_sql)
code used to create the tables
CREATE TABLE symbols
(
symbol_id serial NOT NULL,
symbol varchar(50) NOT NULL,
CONSTRAINT PK_symbols PRIMARY KEY ( symbol_id )
);
CREATE TABLE tweet_symols(
tweet_id varchar(50) REFERENCES tweets,
symbol_id int REFERENCES symbols,
PRIMARY KEY (tweet_id, symbol_id),
UNIQUE (tweet_id, symbol_id)
);
CREATE TABLE hashtags
(
hashtag_id serial NOT NULL,
hashtag varchar(140) NOT NULL,
CONSTRAINT PK_hashtags PRIMARY KEY ( hashtag_id )
);
CREATE TABLE tweet_hashtags
(
tweet_id varchar(50) NOT NULL,
hashtag_id integer NOT NULL,
CONSTRAINT FK_344 FOREIGN KEY ( tweet_id ) REFERENCES tweets ( tweet_id )
);
CREATE INDEX fkIdx_345 ON tweet_hashtags
(
tweet_id
);
The INSERT statement does not define the target columns, so Postgresql will attempt to insert values into a column that was defined as SERIAL.
We can work around this by providing a list of target columns, omitting the serial types. To do this we use SQLAlchemy to fetch the metadata of the table that we are inserting into from the database, then make a list of target columns. SQLAlchemy doesn't tell us if a column was created using SERIAL, but we will assume that it is if it is a primary key and is set to autoincrement. Primary key columns defined with GENERATED ... AS IDENTITY will also be filtered out - this is probably desirable as they behave in the same way as SERIAL columns.
import sqlalchemy as sa
def export_to_sql(df, table_name):
engine = sa.create_engine(f'postgresql://{user}:{password}#{host}:5432/{user}')
df.to_sql(con=engine,
name='temporary_table',
if_exists='append',
index=False,
method='multi')
# Fetch table metadata from the database
table = sa.Table(table_name, sa.MetaData(), autoload_with=engine)
# Get the names of columns to be inserted,
# assuming auto-incrementing PKs are serial types
column_names = ','.join(
[f'"{c.name}"' for c in table.columns
if not (c.primary_key and c.autoincrement)]
)
with engine.begin() as cnx:
insert_sql = sa.text(
f'INSERT INTO {table_name} ({column_names}) (SELECT * FROM temporary_table) ON CONFLICT DO NOTHING; DROP TABLE temporary_table'
)
cnx.execute(insert_sql)
I am importing a csv file containing a parent/child (category-subcategory) hierarchy to MySQL, using Python's MySQLdb module. Here is an example csv file:
vendor,category,subcategory,product_name,product_model,product_price
First vendor,category1,subcategory1,product1,model1,100
First vendor,category1,subcategory2,product2,model2,110
First vendor,category2,subcategory3,product3,model3,130
First vendor,category2,subcategory4,product5,model7,190
In MySQL I want to use a category table with a hierarchical structure, like this:
CREATE TABLE IF NOT EXISTS `category` (
`category_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL DEFAULT '0',
`status` tinyint(1) NOT NULL,
PRIMARY KEY (`category_id`),
KEY `parent_id` (`parent_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
My question is: How do I determine the parent_id in this table?
Here is the Python script I have so far:
import MySQLdb
import csv
con = MySQLdb.connect('localhost', 'root', '', 'testdb', use_unicode=True, charset='utf8')
with con:
cur = con.cursor()
csv_data = csv.reader(file('test.csv'))
csv_data.next()
for row in csv_data:
cur.execute("SELECT manufacturer_id FROM manufacturer WHERE name=%s", [row[0]],)
res = cur.fetchall()
if res:
vendor_id = res[0][0]
else:
cur.execute("INSERT INTO manufacturer (name) VALUES (%s)", (row[0],))
vendor_id = cur.lastrowid
cur.execute("SELECT category_id FROM category_description WHERE name=%s", [row[2]])
res = cur.fetchall()
if res:
category_id = res[0][0]
else:
# What parent_id should be inserted here?
cur.execute("INSERT INTO category (`status`, `parent_id`) VALUES (%s,%s)", (1,))
category_id = cur.lastrowid
cur.execute("INSERT INTO category_description (category_id, name) VALUES (%s,%s)", (category_id,row[2],))
cur.execute("INSERT INTO product (model, manufacturer_id, price,) VALUES (%s, %s, %s)", (row[4], `vendor_id`, row[8],))
product_id = cur.lastrowid
cur.execute("INSERT INTO product_to_category (product_id, category_id) VALUES (%s, %s)", (product_id, category_id,))
cur.commit()
Here are the definitions of the other tables used in my example:
CREATE TABLE IF NOT EXISTS `manufacturer` (
`manufacturer_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
PRIMARY KEY (`manufacturer_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `category_description` (
`category_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`category_id`,`language_id`),
KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `product` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`model` varchar(64) NOT NULL,
`manufacturer_id` int(11) NOT NULL,
`price` decimal(15,4) NOT NULL DEFAULT '0.0000',
PRIMARY KEY (`product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
CREATE TABLE IF NOT EXISTS `product_to_category` (
`product_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL,
PRIMARY KEY (`product_id`,`category_id`),
KEY `category_id` (`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
In a hierarchical table structure, any member at the top of its hierarchy has no parents. I would probably show this with a NULL parent ID but based on the way you've defined your category table, it looks like you want to show this by giving the value 0 for the parent ID.
Since you have fixed-depth hierarchies with only two levels (category and subcategory), the task is relatively simple. For each row of the CSV data, you need to:
Check whether the parent (row[1]) is in the table; if not, insert it with a parent ID of 0.
Get the category_id of the parent from step 1.
Check whether the child (row[2]) is in the table; if not, insert it with a parent ID equal to the category_id from step 2.
In your example code, you never access the parent (row[1]); you need to insert this into the table for it to have an ID that the child can refer to. If you've already inserted the parents before this point, you should probably still check to make sure it's there.
You have some other problems here:
The PK of your category_description table is defined on a column that you forgot to define in the table (language_id).
You should really be using InnoDB in this physical model so that you can enforce foreign key constraints in category_description, product and product_to_category.
In your example, cur.commit() is going to throw an exception – that's a method of the Connection object in MySQLdb. Of course, COMMIT isn't implemented for MyISAM tables anyway, so you could also avoid the exception by removing the line entirely.
Referencing row[8] is also going to throw an exception, according to the CSV data you've shown us. (This is a good example of why you should test your MCVE to make sure it works!)
If you do switch to InnoDB – and you probably should – you can use with con as cur: to get a cursor that commits itself when you exit the with block. This saves a couple lines of code and lets you manage transactions without micromanaging the connection object.
I am getting the below error on trying to do and insert
'Cannot add or update a child row: a foreign key constraint fails
(tvlistings.channelprogramme, CONSTRAINT channelprogramme_ibfk_1
FOREIGN KEY (CHANNELID) REFERENCES channels (CHANNELID))')
A quick google led me to this, but it's a little too specific too the code/query
Cannot add or update a child row: a foreign key constraint fails
The insert query I have is
sqlString = """INSERT INTO CHANNELPROGRAMME (
CHANNELID, PROGRAMMEID, EPISODEID,
RPEAT, NEWSERIES, PREMIERE, CHOICE, SUBTITLES,
DEAFSIGNED, STARRATING, PROGDATE, STARTTIME, ENDTIME,
DURATION)
SELECT
T.CHANNELID, P.PROGRAMMEID, E.EPISODEID, T.RPEAT, T.NEWSERIES,
T.PREMIERE, T.CHOICE, T.SUBTITLES, T.DEAFSIGNED, T.STARRATING,
T.PROGDATE, T.STARTTIME, T.ENDTIME, T.DURATION
FROM
TVTEMPTABLE T
INNER JOIN PROGRAMME P ON P.PROGTITLE=T.PROGTITLE
INNER JOIN EPISODE E ON E.SUBTITLE=T.SUBTITLE AND E.EPISODE=T.EPISODE"""
self._cursor.execute(sqlString)
I am using Python 2.7 and MySQLdb to execute this. I believe the message is saying I am trying to insert a channelid into the channelprogramme table that does not exist the channels table. Unfortunately I am none the wiser on how to fix this?
Here are how the table are defined
CREATE TABLE IF NOT EXISTS CHANNELPROGRAMME (
CHANNELPROGRAMMEID INT NOT NULL AUTO_INCREMENT, CHANNELID INT NOT NULL,
PROGRAMMEID INT NOT NULL, EPISODEID INT NOT NULL, RPEAT BOOL, NEWSERIES BOOL,
PREMIERE BOOL, CHOICE BOOL, SUBTITLES BOOL, DEAFSIGNED BOOL, STARRATING TINYINT,
PROGDATE DATE, STARTTIME TIME, ENDTIME TIME, DURATION INT, PRIMARY KEY(CHANNELPROGRAMMEID),
INDEX (CHANNELID), FOREIGN KEY (CHANNELID) REFERENCES CHANNELS(CHANNELID), INDEX (PROGRAMMEID),
FOREIGN KEY (PROGRAMMEID) REFERENCES PROGRAMME(PROGRAMMEID), INDEX (EPISODEID),
FOREIGN KEY (EPISODEID) REFERENCES EPISODE(EPISODEID)
) ENGINE=INNODB
CREATE TABLE IF NOT EXISTS CHANNELS (
CHANNELID INT NOT NULL, CHANNELNAME VARCHAR(100), INDEX(CHANNELNAME), PRIMARY KEY(CHANNELID)
) ENGINE=INNODB
I'm trying to create a database with several tables connecting to each other using foreign keys using sqlite3, and I'm writing in python.
Here is my code:
db = sqlite3.connect("PHLC.db")
cur = db.cursor()
# ############################
# delete original table if exist
# drop from the end (foreign key issue)
cur.execute("drop table if exists measurement")
cur.execute("drop table if exists mouse")
cur.execute("drop table if exists drug")
cur.execute("drop table if exists batch")
cur.execute("drop table if exists phlc")
# ############################
# create table
# ############################
# 1. phlc
cur.execute(
"""
CREATE TABLE phlc (
phlc_id INTEGER NOT NULL PRIMARY KEY,
cancer VARCHAR(30) NOT NULL,
histology VARCHAR(60) NOT NULL
)
"""
)
# 2. batch
cur.execute(
"""
CREATE TABLE batch (
batch_id INTEGER PRIMARY KEY AUTOINCREMENT,
phlc_id INTEGER NOT NULL,
FOREIGN KEY (phlc_id) REFERENCES phlc (phlc_id),
batch_number INTEGER NOT NULL
)
"""
)
# 3. drug
cur.execute(
"""
CREATE TABLE drug (
drug_id INTEGER PRIMARY KEY AUTOINCREMENT,
drug_name VARCHAR(30) NOT NULL,
batch_id INTEGER NOT NULL,
FOREIGN KEY (batch_id) REFERENCES batch (batch_id)
)
"""
)
# 4. mouse
cur.execute(
"""
CREATE TABLE mouse (
mouse_id INTEGER PRIMARY KEY AUTOINCREMENT,
drug_id INTEGER NOT NULL,
FOREIGN KEY (drug_id) REFERENCES drug (drug_id)
)
"""
)
# 5. measurement
cur.execute(
"""
CREATE TABLE measurement (
measurement_index INTEGER PRIMARY KEY AUTOINCREMENT,
mouse_id INTEGER NOT NULL,
FOREIGN KEY (mouse_id) REFERENCES mouse (mouse_id),
day INTEGER NOT NULL,
tumor_volume FLOAT NOT NULL,
comment VARCHAR(255) NULL
)
"""
)
db.commit()
db.close()
The error I'm getting is at the batch table:
sqlite3.OperationalError: near "batch_number": syntax error
Can someone point out the problem with the code? (It worked fine with MySQL..)
According to the documentation, any table constraints must come after all column definitions:
CREATE TABLE batch (
batch_id INTEGER PRIMARY KEY AUTOINCREMENT,
phlc_id INTEGER NOT NULL,
batch_number INTEGER NOT NULL,
FOREIGN KEY (phlc_id) REFERENCES phlc (phlc_id)
)
Alternatively, make the foreign key declaration a column constraint:
CREATE TABLE batch (
batch_id INTEGER PRIMARY KEY AUTOINCREMENT,
phlc_id INTEGER NOT NULL REFERENCES phlc (phlc_id),
batch_number INTEGER NOT NULL
)