I am currently using Psycopg2 to run 4 separate SQL queries from 4 different tables. The data itself needs to be separated for what I intend to use it for, which is why I am doing it this way. Of the 4 SQL tables that I am pulling from, 3 are under 2mil rows, while the 4th is significantly larger at nearly 24mil rows. It is a very simple statement, basically:
SELECT row1, row2, row3, row4 FROM largetable WHERE row1 = {value};
Which returns usually 10-20 matching rows.
I am designing an app for my coworkers to look up this data and display it via a Tkinter window (which I will leave out of the MCVE). Given what they need to do with it, I need it to populate as fast as possible. The entire load up and populate runs about 10 seconds, with about 5-6 seconds being spent solely on this one SQL. The script grants read-only access to the database, so manipulation of the table is not possible.
Here is an MCVE of the part I need to speed up in my py script. The SQL files all follow the simple outline above but pull from different tables. We can say query_d is the largest.
import psycopg2
from config import config
import tkinter as tk
from tkinter import *
from tkinter.ttk import *
import tkinter.messagebox
def get_val():
class GetValue(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label = tk.Label(text="Input Control")
self.label.config(font=("Ariel", 24))
self.entry = tk.Entry(self)
self.entry.config(font=("Ariel",18),justify='center')
self.button = tk.Button(self, text="Populate",
command=self.on_button)
self.label.pack()
self.entry.pack()
self.button.pack(pady=5)
self.bind("<Return>",self.on_button)
self.bind("<KP_Enter>",self.on_button)
self.entry.focus_force()
def on_button(self, event=None):
global val
try:
val = int(self.entry.get())
except:
tk.messagebox.showerror("Invalid Entry", "Entry must be a
number.")
else:
if control:
conn = None
try:
params = config()
conn = psycopg2.connect(**params)
cur = conn.cursor()
global value
value = {'value':value}
query_a = open("query_a.sql", "r")
a = query_a.read()
a = a.format(**value)
cur.execute(a)
global response_a
response_a = cur.fetchall()
query_a.close()
query_b = open("query_b.sql", "r")
b = query_b.read()
b = b.format(**value)
cur.execute(b)
global response_b
response_b = cur.fetchall()
query_b.close()
query_c = open("query_c.sql", "r")
c = query_c.read()
c = c.format(**value)
cur.execute(c)
global response_c
response_c = cur.fetchall()
query_c.close()
query_d = open("query_d.sql", "r")
d = query_d.read()
d = d.format(**value)
cur.execute(d)
global response_d
response_d = cur.fetchall()
query_d.close()
finally:
if conn is not None:
conn.close()
app = GetValue()
app.mainloop()
if __name__ == '__main__':
get_control()
With these factors in mind, is it possible to speed up this query?
Per #jordanm and #Belayer I have added an index to each table and increased the speed from about 7-8 seconds to about 0.12 seconds.
Related
How do I make the checkboxes stay ticked? I want it to save to a local database. Here is where I gave up:
import sqlite3
import tkinter as tk
from tkinter import ttk
from tkinter import *
from tkinter.ttk import *
from sqlite3 import *
import json
box = Tk()
box.geometry('600x450')
box.title('November Assesment Study List')
box.resizable(False,False)
checkbox1 = tk.StringVar()
checkbox2 = tk.StringVar()
checkbox3 = tk.StringVar()
checkboxes = []
sql_as_text = json.dumps(checkboxes)
checkboxes.append(checkbox1)
checkboxes.append(checkbox2)
checkboxes.append(checkbox3)
connection = sqlite3.connect("Checkedboxes.db")
cursor = connection.cursor()
# cursor.execute("CREATE TABLE Valees (checked integer)")
# query = "INSERT INTO Valees (checked) VALUES (?)"
cursor.execute("INSERT INTO Valees (checked) VALUES )")
# cursor.execute(query, [sql_as_text])
r = cursor.fetchall()
def btn1_checked():
w = checkbox1.get()
checkboxes.append(w)
print(str(checkboxes))
print(r)
def openNewWindow1():
newWindow = Toplevel(box)
newWindow.title("Maths")
newWindow.geometry("400x200")
ttk.Checkbutton(newWindow, text= 'Algebra', command=btn1_checked, variable=checkbox1, onvalue='1', offvalue='0').pack()
ttk.Checkbutton(newWindow, text= 'Calculus', onvalue='1', offvalue='0').pack()
ttk.Checkbutton(newWindow, text= 'Trig', onvalue='1', offvalue='0').pack()
btn = Button(box,
text ="Maths",
command = openNewWindow1)
btn.pack(pady = 10)
cursor.close()
connection.commit()
connection.close()
box.mainloop()
You could also use a local file. Would be a different approach, but then there is no need to install mysql first.
But you were right. You need to store the state of the checkbox somewhere.
checkbox1 = True
f = open("file.txt", "a") # "a" will append to the end of the file.
f.write("checkbox1=" + str(checkbox1) + "\n") # "\n" will create a new paragraph
f.close()
# open read the file
f = open("file.txt", "r")
print(f.read())
Something like this ;)
def nextPage():
conn = sqlite3.connect('xyz.db')
c = conn.cursor()
c.execute("INSERT INTO persons VALUES (?,?)",(text.get(),text1.get()))
conn.commit()
conn.close()
def submit():
conn = sqlite3.connect('xyz.db')
c =conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS persons(
name1 TEXT,
name2 TEXT)''')
conn.commit()
conn.close()
def query():
conn = sqlite3.connect('xyz.db')
c = conn.cursor()
c.execute('SELECT * FROM persons')
records = c.fetchall()
print(records[0])
conn.commit()
conn.close()
import sqlite3
from tkinter import *
from tkinter import ttk
import tkinter as tk
ws = Tk()
ws.geometry('770x400')
ws.title('PythonGuides')
a = Label(ws ,text = "Name").grid(row = 1,column = 0)
b = Label(ws ,text = "Name of Spouse or CP, if applicable").grid(row = 2,column = 0)
text = Entry(ws)
text.grid(row = 1 , column = 1)
text1 = Entry(ws)
text1.grid(row = 2 , column = 1)
btn = ttk.Button(ws ,text="Submit", command = submit).grid(row=4,column=0)
Button(
ws,
text="Next Page", command=nextPage).grid(row = 5,column = 10)
query()
ws.mainloop
#OUTPUT
#('JOHN', '')
I am working on a project using Tkinter and sqlite. Even thou I wanted to print only the 1st item from the list, the whole list was outputted. I want to place the items into individual variables, but each time, the whole list gets stored into a variable. Any help would be appreciated.
I suppose the problem is the line records = c.fetchall(). fetchall() returns a list of tuples, where each tuple is a row from the database. So when you then print(records[0]), you're printing the entire first tuple.
To troubleshoot, try print(records), to print the entire list records, and see what that one looks like. You can then adjust the indexing of your print function to print only the exact stuff you wantt to print.
records = c.fetchall()
print(records[0])
The value stored into records is a list of tuples, so records[0] is a tuple.
In Python tuples are iterables, so that are indexed and are accessibles like lists:
print(records[0][0])
I have an issue that I can´t see clearly and I ask for help here. I don´t know why when I run my code the rows print twice instead of just one. I have 5 rows in my database and when I execute the code it returns 10 rows, the first five and then the five repeated.
from tkinter import *
from tkinter import ttk
from PIL import ImageTk, Image
import sqlite3
class fed:
db_name = 'database.db'
def __init__(self, window):
self.wind = window
self.wind.title('ligth')
self.wind.iconbitmap('doggy.ico')
self.wind.geometry("500x200")
self.get_name()
def run_query(self, query, parameters = ()):
with sqlite3.connect(self.db_name) as conn:
cursor = conn.cursor()
result = cursor.execute(query, parameters)
conn.commit()
return result
def get_name(self):
query = 'SELECT * FROM name'
db_rows = self.run_query(query)
for row in db_rows:
print(row)
if __name__ == '__main__':
window = Tk()
fed(window)
application = fed(window)
window.mainloop()
You are running the class twice, on the 3rd and 2nd to last lines. Remove one of your fed(window) calls.
I have a GUI that contains all the database records. Upon clicking the "search" button, I want all the records to be cleared from the screen, and the records relevant to the user's search to appear. I am trying to reference my labels and call the "destroy" method like this: a.destroy() but the program is not recognizing these labels as widgets.
class DBViewer:
def __init__(self, rows):
# Display all records
row_for_records = 2
for record in rows:
a = tkinter.Label(self.main_window, text=record[0])
a.grid(row=row_for_records, column=0)
b = tkinter.Label(self.main_window, text=record[1])
b.grid(row=row_for_records, column=1)
# More labels here
self.display_rows(rows)
tkinter.mainloop()
def display_rows(self, rows):
# Where I'm having trouble
def main():
global db
try:
dbname = 'books.db'
if os.path.exists(dbname):
db = sqlite3.connect(dbname)
cursor = db.cursor()
sql = 'SELECT * FROM BOOKS'
cursor.execute(sql)
rows = cursor.fetchall()
DBViewer(rows)
db.close()
Edit: Separated widget assignment and grid but still facing have the same problem. Anyone want to help out a programming newbie?
In order to delete something, you must have a reference to it. You aren't saving references to the widgets you are creating. Try saving them to a list or dictionary.
When creating them:
...
self.labels = []
for record in rows:
a = Tkinter.Label(...)
self.labels.append(a)
...
When you want to delete them:
for label in self.labels:
label.destroy()
from tkinter import *
import tkinter as tk
import pyodbc
root1 = tk.Tk()
label1 = tk.Label(root1, text='product A')
input1 = StringVar()
entry1 = tk.Entry(root1,textvariable=input1)
label1.pack(side = tk.TOP)
entry1.pack()
buttonstr = tk.StringVar()
db = r"C:\Users\Goutham\Documents\keshav\testdb.accdb"
def odbc():
'''
connects with odbc
'''
constr = 'Driver={Microsoft Access Driver (*.mdb, *.accdb)};Dbq=' + db
conn = pyodbc.connect(constr, autocommit=True)
cur = conn.cursor()
check=input1.get()
strsql = "select * from student where SName=%s"%(check)
cur.execute(strsql)
results = cur.fetchall()
print (results,check)
conn.close()
buttonA = tk.Button(text = "hello", command = odbc)
buttonA.pack()
I need this code to get input,store it in the variable -'check' and compare it with the values in the database using a SQL query.The matching values from the database are then displayed.
There seems to be a problem in the implementation of the SQL query.'check' stores the value of the input. The SQL query does not work properly and causes errors.
Please help.
Thank you.
You need to single quote the parameter to the WHERE clause:
strsql = "select * from student where SName='%s'" % (check,)
But be careful with building clauses like this (using string formatting), you run the risk of SQL injection. You should pass parameters instead:
strsql = "select * from student where SName=?"
cur.execute(strsql, (check,))