Python - automating MySQL index: passing parameter - python

I have a function with a new improved version of the code for automatic table indexing:
def update_tableIndex(self,tableName):
getIndexMySQLQuery = """SELECT numberID
FROM %s;""" % (tableName,)
updateIndexMySQLQuery = """UPDATE %s
SET numberID=%s WHERE numberID=%s;""" % (tableName,)
updateIndex=1
self.cursorMySQL.execute(getIndexMySQLQuery)
for row in self.cursorMySQL:
indexID = row[0]
self.cursorMySQL.execute(updateIndexMySQLQuery,(updateIndex,indexID))
updateIndex+=1
While the query 'getIndexMySQLQuery' works fine with this syntax, the other one 'updateIndexMySQLQuery' doesn't work.
Any hints or suggestion how to get that fixed?
All comments and suggestions are highly appreciated.

Second one doesn't work, because you are using three placeholders inside the query string and provide only one variable for interpolation.
updateIndexMySQLQuery = """UPDATE %s
SET numberID=%%s WHERE numberID=%%s;""" % (tableName,)
This way the string formatting mechanism doesn't expect you to provide 3 values, as the percent signs are "escaped" (shame on me for the first version of the answer).

Use %s to replace the table name in the beginning, but use a question mark to create a parameter replacement.
updateIndexMySQLQuery = """UPDATE %s
SET numberID=? WHERE numberID=?;""" % (tableName,)
...
self.cursorMySQL.execute(updateIndexMySQLQuery,(updateIndex,indexID))

thanks for the input. I just re-did the whole function. Here is how it's working and looks now:
def update_tableIndex(self,tableName,indexName):
getIndexMySQLQuery = """SELECT %s
FROM %s;""" % (indexName,tableName,)
updateIndex=1
self.cursorMySQL.execute(getIndexMySQLQuery)
for row in self.cursorMySQL:
indexID = row[0]
updateIndexMySQLQuery = """UPDATE %s
SET %s=%s WHERE
%s=%s;""" % (tableName,
indexName,updateIndex,
indexName,indexID)
self.cursorMySQL.execute(updateIndexMySQLQuery)
updateIndex+=1
So, the only thing to do is to inform the column name and the table name as parameters. It allows to re-use the code for all other tables in the database.
Hope this can be useful for others too.

Related

Python updating a sql DB

Have a program that recieves a list of player names and results. Then trys to add them to a SQL DB. If the player name is already on the list I want it to update there Score by adding there current score to the stored one. I'm trying to do this by using the ON DUPLICATE KEY UPDATE statement but I'm unsure how to tell it to use the value. Here is my code.
from mysql.connector.cursor import MySQLCursorPrepared
print(playerNames ,playerScore )
try:
#MariaDB Connection
con = mysql.connector.connect(port=5004,user='root',password='password',host='localhost',database='scoreboard')
records_to_insert = [ (playerNames[0],playerScore[0]) ,
(playerNames[1],playerScore[1]),
(playerNames[2],playerScore[2]) ,
(playerNames[3],playerScore[3])
]
sql_insert_query = " INSERT INTO results (Player, Score) VALUES (%s,%s) ON DUPLICATE KEY UPDATE Score = VALUES(Score) + %s; "
myCursor = con.cursor()
myCursor.executemany(sql_insert_query, records_to_insert)
con.commit()
I know the issue is with using "= VALUES(Score) + %s" but I'm not sure of how else to tell it what value I want to use there. Any advice would be greatly appreciated.
You can simply use this expression:
INSERT INTO results (Player, Score)
VALUES (%s, %s)
ON DUPLICATE KEY UPDATE Score = VALUES(Score) + Score;
Notes:
This assumes that you have a unique index on Player (or better yet, it is the primary key).
You should learn to use SQL parameters for passing in values, rather than string substitution.
You might want to declare Score to be NOT NULL to ensure that it always has reasonable values.
sql_insert_query = " INSERT INTO results (Player, Score) VALUES {0},{1}) ON DUPLICATE
KEY UPDATE Score = VALUES(Score) + {1}; ".format(playerNames[0],playerScore[1])

Inserting data using subqueries and Python

I'm a bit new to the whole Python + DB interactions and I have encountered some issues while trying to insert into a table using subqueries. At this point I imagine it's only a problem of syntax/missing parentheses/missing exclamation marks/etc but I can't seem to be able to figure it out by myself so maybe a fresh eye could help with the issue.
This is the query I am trying to run :
self.cur.execute("INSERT INTO game_genre (id_game, id_genre) VALUES (( SELECT gd.id_game from game_details gd where gd.title like %s, ( "%" + x + "%" , )) , (SELECT g.id_genre from genres g where g.title_genre like %s, ("%" + genre + "%",))"))
where "x" and "genre" are variables
I have tested the queries independently (outside of the insert) and they return the expected result
Could someone shed some divine light onto this?? Thanks
It looks like you're trying to parameterize your query, but you've stuck two different parameter lists into your query as postgres code instead of just one after your query as python code. It's always a good idea to format your code so that the overall structure jumps out at you.
Try:
self.cur.execute("""
INSERT INTO game_genre (id_game, id_genre)
VALUES (
(SELECT gd.id_game FROM game_details gd WHERE gd.title LIKE %s),
(SELECT g.id_genre FROM genres g WHERE g.title_genre LIKE %s))
""", ("%" + x + "%", "%" + genre + "%"))
See http://initd.org/psycopg/docs/usage.html for more details on query parameterization. In particular, note that execute() takes a query with a bunch of %ss, and then a parameter list with one value for each %s. Using """ around every query is highly recommended, in part for readability, and in part because it will prevent you from having to escape " and ' characters.

Cannot validate query results

query = "SELECT serialno from registeredpcs where ipaddress = "
usercheck = query + "'%s'" %fromIP
#print("query"+"-"+usercheck)
print(usercheck)
rs = cursor.execute(usercheck)
print(rs)
row = rs
#print(row)
#rs = cursor.rowcount()
if int(row) == 1:
query = "SELECT report1 from registeredpcs where serialno = "
firstreport = query + "'%s'" %rs
result = cursor.execute(firstreport)
print(result)
elif int(row) == 0:
query_new = "SELECT * from registeredpcs"
cursor.execute(query_new)
newrow = cursor.rowcount()+1
print(new row)
What I am trying to do here is fetch the serialno values from the db when it matches a certain ipaddress. This query if working fine. As it should the query result set rs is 0. Now I am trying to use that value and do something else in the if else construct. Basically I am trying to check for unique values in the db based on the ipaddress value. But I am getting this error
error: uncaptured python exception, closing channel smtpd.SMTPChannel connected
192.168.1.2:3630 at 0x2e47c10 (**class 'TypeError':'int' object is not
callable** [C:\Python34\lib\asyncore.py|read|83]
[C:\Python34\lib\asyncore.py|handle_read_event|442]
[C:\Python34\lib\asynchat.py|handle_read|171]
[C:\Python34\lib\smtpd.py|found_terminator|342] [C:/Users/Dev-
P/PycharmProjects/CR Server Local/LRS|process_message|43])
I know I am making some very basic mistake. I think it's the part in bold thats causing the error. But just can't put my finger on to it. I tried using the rowcount() method didn't help.
rowcount is an attribute, not a method; you shouldn't call it.
"I know I am making some very basic mistake" : well, Daniel Roseman alreay adressed the cause of your main error, but there are a couple other mistakes in your code:
query = "SELECT serialno from registeredpcs where ipaddress = "
usercheck = query + "'%s'" % fromIP
rs = cursor.execute(usercheck)
This part is hard to read (you're using both string concatenation and string formatting for no good reason), brittle (try this with `fromIP = "'foo'"), and very very unsafe. You want to use paramerized queries instead, ie:
# nb check your exact db-api module for the correct placeholder,
# MySQLdb uses '%s' but some other use '?' instead
query = "SELECT serialno from registeredpcs where ipaddress=%s"
params = [fromIP,]
rs = cursor.execute(query, params)
"As it should the query result set rs is 0"
This is actually plain wrong. cursor.execute() returns the number of rows affected (selected, created, updated, deleted) by the query. The "resultset" is really the cursor itself. You can fetch results using cursor.fetchone(), cursor.fetall(), or more simply (and more efficiently if you want to work on the whole resultset with constant memory use) by iterating over the cursor, ie:
for row in cursor:
print row
Let's continue with your code:
row = rs
if int(row) == 1:
# ...
elif int(row) == 0:
# ...
The first line is useless - it only makes row an alias of rs, and badly named - it's not a "row" (one line of results from your query), it's an int. Since it's already an int, converting it to int is also useless. And finally, unless 'ipadress' is a unique key in your table, your query might return more than one row.
If what you want is the effective value(s) for the serialno field for records matching fromIP, you have to fetch the row(s):
row = cursor.fetchone() # first row, as a tuple
then get the value, which in this case will be the first item in row:
serialno = row[0]

How to add a row number column to an SqlAlchemy query?

I would like to have the row number as a column of my queries. Since I am using MySql, I cannot use the built-in func.row_number() of SqlAlchemy. The result of this query is going to be paginated, therefore I would like to keep the row number before the split happen.
session.query(MyModel.id, MyModel.date, "row_number")
I tried to use an hybrid_property to increment a static variable inside MyModel class that I reset before my query, but it didn't work.
#hybrid_property
def row_number(self):
cls = self.__class__
cls.row_index = cls.row_index + 1
return literal(self.row_index)
#row_number.expression
def row_number(cls):
cls.row_index = cls.row_index + 1
return literal(cls.row_index)
I also tried to mix a subquery with this solution :
session.query(myquery.subquery(), literal("#rownum := #rownum + 1 AS row_number"))
But I didn't find a way to make a textual join for (SELECT #rownum := 0) r.
Any suggestions?
EDIT
For the moment, I am looping on the results of the paginated query and I am assigning the calculated number from the current page to each row.
SQLAlchemy allows you to use text() in some places, but not arbitrarily. I especially cannot find an easy/documented way of using it in columns or joins. However, you can write your entire query in SQL and still get ORM objects out of it. Example:
query = session.query(Foobar, "rownum")
query = query.from_statement(
"select foobar.*, cast(#row := #row + 1 as unsigned) as rownum"
" from foobar, (select #row := 0) as init"
)
That being said, I don't really see the problem with something like enumerate(query.all()) either. Note that if you use a LIMIT expression, the row numbers you get from MySQL will be for the final result and will still need to have the page start index added. That is, it's not "before the split" by default. If you want to have the starting row added for you in MySQL you can do something like this:
prevrow = 42
query = session.query(Foobar, "rownum")
query = query.from_statement(sqlalchemy.text(
"select foobar.*, cast(#row := #row + 1 as unsigned) as rownum"
" from foobar, (select #row := :prevrow) as init"
).bindparams(prevrow=prevrow))
In this case the numbers will start at 43 since it's pre-incrementing.

Python SQL select row from specific variable field

I'd like to grab a specific value from a row based on a random variable. Here's an example table the PID column is an "auto-increment primary key integer" and the other 2 columns are TEXT
example-table
PID NAME PHONE
--- ---- -----
1 bill 999-9999
2 joe 888-8888
I'd like to throw a random variable at the table
randomVariable = raw_input('Enter something: ')
> 1
and have the code return the name
> bill
I know I can use something like...
randomVariable = raw_input('Enter something: ')
sql = ("SELECT name FROM example_table WHERE pid='%s'" % randomVariable)
result = cursor.execute(sql)
print result
> bill
Apparently using '%s' isn't secure and it is suggested to use '?' in it's place.
randomVariable = raw_input('Enter something: ')
sql = ("SELECT name FROM example_table WHERE pid=?", randomVariable)
result = cursor.execute(sql)
print result
But this doesn't seem to work for me. I end up with...
"ValueError: operation parameter must be str or unicode"
I realize I could just grab all the rows and put them into a variable which I could then iterate over till I find what I'm looking for but I'm thinking that wouldn't be very efficient with a large database. can anyone help point me in the right direction with this?
I believe you're meant to use it like this
randomVariable = raw_input('Enter something: ')
sql = "SELECT name FROM example_table WHERE pid=?"
result = cursor.execute(sql, randomVariable)
print result
Validate the user input, and %s is fine. Storing your rows and putting them into a list is not a good idea at all, since the amount of rows will grow over time, taking up a huge amount of memory when not even in use. To guard against SQL injection, you could validate input using something like a typecast to an int, put that in a try/except block, and this would stop all malicious input such as ' OR 1=1--

Categories