List index out of range for Python - python

I have a question regarding the following exercise:
def addstock():
time = datetime.now().strftime("%B %d, %Y")
hour = datetime.now().strftime("%I:%M%p")
query = 'SELECT TotalStock FROM Stocks WHERE name = ? ORDER BY MovementID DESC LIMIT 1'
parameters = (name.get(),)
lastrecord = run_query(query, parameters)
print(float(lastrecord.fetchall()[0][0]))
print(float(quantity.get()))
totalstock = (float(lastrecord.fetchall()[0][0])) + (float(quantity.get()))
query = 'SELECT precio FROM product WHERE name = ?'
precio = run_query(query, parameters)
pricequant = precio.fetchall()[0]
pricequantity = pricequant * quantities
query = 'SELECT precio FROM product WHERE name = ?'
parameters = (name.get(),)
precio = run_query(query, parameters)
priceforall = pricequant * totalstock
In this function, I print lastrecord.fetchall()[0][0] and quantity.get to make sure they are float. So the program prints in that case: 5.0 for lastrecord.fetchall and quantity.get
Up to now, no problem, but when I try to us them up, it gives me an error of List Index Out Of Range, so program do not find the value of lastrecord.fetchall()[0][0] which 2 lines before I was able to print successfully. Can someone explain why?

According to documentation:
The method fetches all (or all remaining) rows of a query result set and returns a list of tuples. If no more rows are available, it returns an empty list.
When you first time used lastrecord.fetchall()[0][0] all the records of lastrecord curser are fetched, so on the second call on totalstock = (float(lastrecord.fetchall()[0][0])) + (float(quantity.get())) there is no more rows left for the curser. If you want to reuse the fetched data, store it, then use it anytime you want, like this:
all_records = lastrecord.fetchall()
// ...
print(float(all_records[0][0]))
// ...
totalstock = (float(all_records[0][0])) + (float(quantity.get()))

Related

Passing multiple arguments in SQL - Python

I am trying to pass two arguments into a SQL statement as below:
cursor.execute(f"""select * from table
where product_name = '{prod_name}' and date = '{sale_date}'"""")
I am trying to have this run through a loop for several combination so I am trying to see how I can have this altered accordingly.
prod_name = ['prod_a','prod_b']
sale_date = ['2020-01-01','2020-02-01']
I know how to pass one argument through a loop but I am not sure how to pass more than one argument together at the same.
It's a security danger to add variables directly to your SQL query. cursor.execute provides sanitizing as long as you pass the arguments as the second argument of the function call.
Example:
cursor.execute("select * form table where product_name = '%s' and date = '%s'", (prod_name, sale_date))
To loop through multiple lists at once you can do the following (assuming the lists have the same amount of values):
for i in range(len(prod_name)):
cursor.execute("select * form table where product_name = '%s' and date = '%s'", (prod_name[i], sale_date[i]))
By looping through a range I get the numbers of 0 - len(prod_name) and as I loop with the index of i I can use that to retrieve the first item in both lists.
Sam Mason had a good comment about using the zip function which combines iterators and can be used like so:
for args in zip(prod_name, sale_date):
cursor.execute("select * form table where product_name = '%s' and date = '%s'", args)
try this :
results = ()
dc = ['103,4770634', '42,427752', '64,10122045', '42,13603629', '42,25516425', '103,2748102', '42,1966402', '42,30262834', '42,6667711', '18,13737683', '42,28921168', '42,26076925', '103,3733654', '42,23313527', '64,3307344', '103,3973533', '42,6360982', '48,11846077', '103,3775309', '64,10122050', '42,1965119', '103,4265810', '103,3971645', '103,4962583', '103,689615', '42,22834366', '103,761655', '95,1184', '64,9594482', '42,22855603', '48,8654764', '103,4226756', '42,23366982', '103,3897036', '42,11339650', '101,6369', '42,25830920', '103,5009291', '42,29238961', '59,6299475', '42,22931663', '42,25839056', '43,11864458', '43,41346192', '103,4261645', '42,3747082', '103,4795050', '42,9417503', '103,4245623', '42,61431911']
try:
sql = "SELECT * FROM tbl1 WHERE id1 in (%s) AND id2 in (%s)"
in_ids = ', '.join(map(lambda x: '%s', dc))
in_ids = in_ids % tuple(dc)
sql = sql % (in_ids, in_ids)
cursor.execute(sql)
res = cursor.fetchall()
results = results + res
except Exception, e:
print e

Query a mysql table for a dynamic column name and its value in python?

I want to search a mysql table for rows where the specified column has a particular value. For example, given the input string memory=2048 it will search for the rows that have "2048" as the value of memory column and it will print them.
This is code that I have tried but it print outs nothing.
input = input()
tag = input.split("=")
desc = tag[1]
tag = tag[0]
mycursor = mydb.cursor()
sql = "(SELECT * FROM comp WHERE %s LIKE %s)"
val = (tag, desc)
mycursor.execute(sql, val)
res = mycursor.fetchall()
for x in res:
print(x)
Secondly I tried this code to see where is the problem :
input = input()
tag = input.split("=")
desc = tag[1]
tag = tag[0]
mycursor = mydb.cursor()
sql = "(SELECT * FROM comp WHERE memory LIKE '2048')"
mycursor.execute(sql)
res = mycursor.fetchall()
for x in res:
print(x)
It gives the desired output. So my problem is when I am trying to get the column name with %s it comes as 'memory' and It couldn't finds it, since the name of the column is memory. Is there a way to get rid of the '' chars ?
confirmation of inputs
Looking at the mysql.connector's execute() documentation it appears to use %s as placeholders for bind parameters.
So your execute("SELECT * FROM comp WHERE %s LIKE %s", ("memory", "2048")) call ends up running like the following SQL:
SELECT * FROM comp WHERE 'memory' LIKE '2048'
obviously returning 0 rows.
You need to put the literal column name into the query text before invoking execute():
sql = "SELECT * FROM comp WHERE %s LIKE %s" % (tag, "%s")
# => "SELECT * FROM comp WHERE memory LIKE %s"
mycursor.execute(sql, (desc, ))

How to execute more than once the same query with different data?

I'm trying to execute the same query but with different data but I always get data the first time. The others times, dispite of there are data for the querys in the data base, mysql returns empty data.
This is the code:
def get_team_colour_map(self, players, id_competition):
tcm = FIBAColourMap()
for p in players:
args = [p["id"], id_competition]
conn = pymysql.Connect(host = DDBB.DDBB_FIBA_HOST,
user = DDBB.DDBB_FIBA_USER,
password = DDBB.DDBB_FIBA_PSWD,
db = DDBB.DDBB_FIBA_NAME,
charset = DDBB.DDBB_FIBA_CHARSET,
cursorclass=pymysql.cursors.DictCursor)
with conn.cursor() as cursor:
print("id player: {}".format(p["id"]))
print("args: {}".format(args))
cursor.execute("select sc.* from tbl030_shots_chart sc, tbl006_player_team pt, tbl007_game g, tbl004_jornada j, tbl012_competition c where pt.id = %s and pt.id_player_feb = sc.id_fiba and sc.id_game = g.id and g.id_jornada = j.id and j.id_competition = c.id and c.id = %s", args)
data = cursor.fetchall()
print("data: {}".format(data))
print("Total rows: {}".format(cursor.rowcount))
if cursor.rowcount > 0:
for s in data:
x = float(FIBASCReport.adjust_x(s["x"]))
y = float(FIBASCReport.adjust_y(s["y"]))
color = tcm.image.getpixel((x,y))
color = ("#%02x%02x%02x" % color).upper()
if tcm.exists_color(color):
if int(s["m"]) == 0:
tcm.set_scored_shots(color, 1)
else:
tcm.set_failed_shots(color, 1)
else:
if int(s["m"]) == 0:
tcm.set_scored_shots("OTROS", 1)
else:
tcm.set_failed_shots("OTROS", 1)
else:
#tcm = None
print("Jugadora con id: {} NO ha realizado ningún tiro en competición: {}".format(p["id"], id_competition))
return tcm
In this code, cursor.fetchall() returns data the first query but the next querys returns empty results.
How can I run several querys? I'm using mySQL 8.0 and Python 3.6
Its because you are using the same cursor each time. create a new instance of the cursor each time you loop through to excecute the query. After the first query is run the cursor is already positioned after all the data. Hence no rows returned after that
You can also try this:
Look at the documentation for MySQLCursor.execute().
It claims that you can pass in a multi parameter that allows you to run multiple queries in one string.
If multi is set to True, execute() is able to execute multiple statements specified in the operation string.
multi is an optional second parameter to the execute() call:
operation = 'SELECT 1; INSERT INTO t1 VALUES (); SELECT 2'
for result in cursor.execute(operation, multi=True):

Using DESC within MAX ID Sqlite3 - Python

Can someone tell me why the code below is throwing errors. Its meant to look at the current id and if its less the max to descend to the next row in the database and print it.
def loadData(self):
connection = sqlite3.connect('films.db')
c = connection.cursor()
maxid_before = c.execute("SELECT * FROM FILMS WHERE ID = (SELECT MAX(ID) FROM FILMS)")
last_row = maxid_before.fetchone()[0]
maxid_after = c.execute("SELECT * FROM FILMS WHERE ID < (last_row) ORDER BY ID DESC LIMIT 1;").fetchone()
print(maxid_after)
UPDATE
This is the function that calls loadData.What silly mistake have i done this time.You can ignore the first 5 lines or so.
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.edit_btn.clicked.connect(self.Retrieve)
self.edit_save_btn.clicked.connect(self.insertData)
last_id = 0
if not last_id:
connection = sqlite3.connect('films.db')
c = connection.cursor()
result = c.execute("SELECT * FROM FILMS WHERE ID = (SELECT MAX(ID) FROM FILMS)")
last_id = result.fetchone()[0]
self.edit_load_btn.clicked.connect(self.loadData)
And this is the loadData function
def loadData(self, last_id):
connection = sqlite3.connect('films.db')
c = connection.cursor()
maxid_after = c.execute("SELECT * FROM FILMS WHERE ID < ? ORDER BY ID DESC LIMIT 1;", (last_id,)).fetchone()
print(maxid_after)
return maxid_after
Try this -
maxid_after = c.execute("SELECT * FROM FILMS WHERE ID < ? ORDER BY ID DESC LIMIT 1;", (last_row,)).fetchone()
In your code last_row is a variable not column name. So, you should put the value of last_row in the query.
If you put last_row in the query like you did, sqlite will treat last_row as a column name and try to execute the query like that. As it fails to find any column by that name, it throws the error.
If you want to run this function on a button click and return one by one row in descending order in every button click, then your function should be like this -
def loadData(self):
connection = sqlite3.connect('films.db')
c = connection.cursor()
result = c.execute("SELECT * FROM FILMS WHERE ID < ? ORDER BY ID DESC LIMIT 1;", (last_id,)).fetchone()
if result:
self.last_id = result[0]
connection.close()
And the function that calls loadData() should be like -
# This is the code that calls loadData()
# This is __init__
result = c.execute("SELECT * FROM FILMS WHERE ID = (SELECT MAX(ID) FROM FILMS)").fetchone()
if result:
self.last_id = result[0]
...
# On button click
# Call loadData

Python put Database records in Namedtuple

I'm trying to write some code in python (2.7) doing this:
Open a database in sqlite
Do a query to the database, getting some results. The database has more than one table and i need the records from different tables:
database is like this:
data.db ---> [table1[col1, col2, col3], table2[col1, col2, col3]]
Iterate through the results
Do something on the results (for exaple in a record there is a date that need decoding)
Store all the records in a namedtuple for further access
Since now i have achieved part 1, 2, 3 and 4, but i can't figure out how to store the results in a namedtuple. Assume that on every iteration i store the data that i need in the namedtuple in temporary variables:
for result in results:
var1 = table1.col1
var2 = table2.col1
var3 = table3.col1
(now i want to do something with the variable, but thats' not the problem, and store the 3 variables in a namedtuple)
contacts = namedtuple('contacts', 'Z_PK ZFULLNAME ZPHONE ZTEXT ZDATE')
if has_contacts_sqlite:
# reading contacts from database
# 1st step: ZWAPHONE table
query = "SELECT * FROM ZWAPHONE"
self.tempcur.execute(query)
tempcontacts = self.tempcur.fetchall()
for contact in tempcontacts:
id = contact.Z_PK
contact_key = contact.ZCONTACT
favorite_key = contact.ZFAVORITE
status_key = contact.ZSTATUS
phonenum = contact.ZPHONE
# 2nd step: name from ZWACONTACT table
query = "SELECT * FROM ZWACONTACT WHERE Z_PK=?;"
self.tempcur.execute(query, [contact_key])
contact_entry = self.tempcur.fetchone()
if contact_entry == None:
name = "N/A"
else:
name = contact_entry.ZFULLNAME
# 3rd step: status from ZWASTATUS table
query = "SELECT * FROM ZWASTATUS WHERE Z_PK=?;"
self.tempcur.execute(query, [status_key])
status_entry = self.tempcur.fetchone()
if status_entry == None:
text = "N/A"
date = "N/A"
else:
text = status_entry.ZTEXT
date = self.formatDate(status_entry.ZDATE)
#print ("%s" %(id))
#print ("%s" %(name.encode(sys.stdout.encoding, errors='replace')))
#print ("%s" %(phonenum.encode(sys.stdout.encoding, errors='replace')))
#print ("%s" %(text.encode(sys.stdout.encoding, errors='replace')))
#print ("%s" %(date))
contact = contacts(id, name, phonenum, text, date)
print contacts
for line in contacts:
print line
A namedtuple() is nothing but a class generated with a factory function:
SomeRowResult = namedtuple('SomeRowResult', 'var1 var2 var3')
Here SomeRowResult is a class object (a subclass of tuple), and calling it will create instances of the class:
for result in results:
result = SomeRowResult(table1.col1, table2.col1, table3.col1)
If you wanted to have a list of these results, you need to explicitly build that list:
all_results = []
for result in results:
result = SomeRowResult(table1.col1, table2.col1, table3.col1)
all_results.append(result)

Categories

Resources