I am working on a GUI gird and sql. I have two GUI buttons that can be clicked depending on what order the user wants the information. The order can be by Employee Last_name or First_name, but not both. I am not sure how to use that. I am suppose to use concatenation, but am not sure how to.
Below is what I tried to do:
def sort_employees(self, column):
try:
cursor = db.cursor()
display="SELECT * FROM company ORDER BY '%" + column + "%' "
cursor.execute(display)
entry = cursor.fetchall()
self.display_rows(entry)
Also, the code works fine if I only have on entry:
display="SELECT * FROM company ORDER BY Last_name"
Not sure why you have % in your query string, it's possible you're confusing it with the %s syntax for string formatting.
display = "SELECT * FROM company ORDER BY '%" + column + "%' "
It seems what you want is more like this:
display = "SELECT * FROM company ORDER BY " + column
Or, as I prefer:
display = 'SELECT * FROM company ORDER BY {column}'.format(column=column)
Of course be careful creating queries like this, you're exposed to SQL security vulnerabilities.
It's better to use a parametrised query instead of string interpolation/concatenation, but I don't know which database interface you're using, but it's easy to find that by searching the docs.
In SQL, the ORDER BY clause takes, as arguments, a list of column names:
--correct
ORDER BY firstname, lastname, age
It can also take function outputs:
--correct, sort names beginning with a Z first
ORDER BY CASE WHEN first name LIKE 'Z%' THEN 1 ELSE 2 END, firstname
In some db, putting an integer ordinal on will sort by that column, numbered from the left, starting with 1:
--correct, sort by 3rd column then first
ORDER BY 3,1
It does not take a list of strings that happen to contain column names:
--incorrect - not necessarily a syntax error but will not sort by any named column
ORDER BY 'firstname', 'lastname', 'age'
Nor does it take a string of csv column names:
--incorrect - again not necessarily a syntax error but won't sort on any of the named columns
ORDER BY 'firstname, lastname, age'
Your code falls into the latter categories: you're turning the column name into a string. This is wrong. The "not working sql" and the "working sql" are very different. Print the result of he concatenation to screen and look at them if you're having a hard time seeing it from the code
Related
I am coding with Python 3.8.5, and mysql Ver 15.1 Distrib 10.4.11-MariaDB.
I have three tables, customer, partner and customer_partner,
customer has columns customer_id, customer_name, address;
partner has columns partner_id, partner_name, address; (note the address column appears in both tables, but obviously different content)
customer_partner has columns customer_id, partner_id, describing the partnership between one customer and one partner;
I am trying to fetch joined columns of customer and partner for customers whose customer_id is in a list with following python code and SQL statement:
db = connect(...)
cur = db.cursor(dictionary=True)
customer_id_tuple = (1, 2, 3)
sql = f"""SELECT *
FROM customer, partner, customer_partner
WHERE
customer.customer_id in ({','.join(['%s' for _ in range(len(customer_id_list))])})
AND customer.customer_id=customer_partner.customer_id
AND customer_partner.partner_id=partner.partner_id
"""
cur.execute(sql, customer_id_tuple))
data = cur.fetchall()
In the result dictionary data, I only see one address column. Obviously, address from partner table overwrites the one from customer table.
Besides modifying the column names, do I have a more decent way to avoid such overwriting behavior? Like automatically inserting the table name in front of the column name, like customer.address and partner.address?
SELECT * ... may lead to ambiguities when there are conflicting column names.
You should set aliases for conflicting column names.
Also set short aliases for the table names that can shorten the code and make it more readable and use them to qualify all the column names.
The implicit join syntax that you use has been replaced, since many years, by explicit join syntax.
Your code should be written like this:
sql = f"""
SELECT c.customer_id, c.customer_name, c.address customer_address,
p.partner_id, p.partner_name, p.address partner_address
FROM customer c
INNER JOIN customer_partner cp ON c.customer_id = cp.customer_id
INNER JOIN partner p ON cp.partner_id = p.partner_id
WHERE c.customer_id IN ({','.join(['%s' for _ in range(len(customer_id_list))])})
"""
I left out all the columns of customer_partner from the SELECT list because they are not needed.
Let's say mytable has 5 columns, id being the first one.
Is it possible to do an UPDATE without hardcoding the column names?
UPDATE mytable VALUES(4, "hello", 31.12, 4141.12, "gjhg") WHERE id = 4
I haven't found it in most tutorials.
Desired use case with sqlite3:
row = (4, "hello", 31.12, 4141.12, "gjhg")
c.execute('UPDATE mytable VALUES(?) WHERE id = ?', row, row[0])
As far as I know you cannot do that. According to https://sqlite.org/lang_update.html
However there are other ways of writing the query:
https://stackoverflow.com/a/13482298/7791653
You could generate a query yourself.
First you select all the column names and do a for loop where you build a String like "column1, column1, column1" and add that to the appropriate place of the query. All you have to do then is something like
"UPDATE users
SET (" + generatedQueryPart = ")
= ('value1', 'value2', 'value3')
WHERE some_condition ";
Hope this gave you some more information.
I am quite new to SQL, but trying to bugfix the output of an SQL-Query. However this question does not concern the bug, but rather why SQLite3 does not yield an error when it should.
I have query string that looks like:
QueryString = ("SELECT e.event_id, "
"count(e.event_id), "
"e.state, "
"MIN(e.boot_time) AS boot_time, "
"e.time_occurred, "
"COALESCE(e.info, 0) AS info "
"FROM events AS e "
"JOIN leg ON leg.id = e.leg_id "
"GROUP BY e.event_id "
"ORDER BY leg.num_leg DESC, "
"e.event_id ASC;\n"
)
This yields an output with no errors.
What I dont understand, is why there is no error when I GROUP BY e.event_id and e.state and e.time_occurred does not contain aggregate-functions and is not part of the GROUP BY statement?
e.state is a string column. e.time_occurred is an integer column.
I am using the QueryString in Python.
In a misguided attempt to be compatible with MySQL, this is allowed. (The non-aggregated column values come from some random row in the group.)
Since SQLite 3.7.11, using min() or max() guarantees that the values in the non-aggregated columns come from the row that has the minimum/maximum value in the group.
SQLite and MySQL allow bare columns in an aggregation query. This is explained in the documentation:
In the query above, the "a" column is part of the GROUP BY clause and
so each row of the output contains one of the distinct values for "a".
The "c" column is contained within the sum() aggregate function and so
that output column is the sum of all "c" values in rows that have the
same value for "a". But what is the result of the bare column "b"? The
answer is that the "b" result will be the value for "b" in one of the
input rows that form the aggregate. The problem is that you usually do
not know which input row is used to compute "b", and so in many cases
the value for "b" is undefined.
Your particular query is:
SELECT e.event_id, count(e.event_id), e.state, MIN(e.boot_time) AS boot_time,
e.time_occurred, COALESCE(e.info, 0) AS info
FROM events AS e JOIN
leg
ON leg.id = e.leg_id "
GROUP BY e.event_id
ORDER BY leg.num_leg DESC, e.event_id ASC;
If e.event_id is the primary key in events, then this syntax is even supported by the ANSI standard, because event_id is sufficient to uniquely define the other columns in a row in events.
If e.event_id is a PRIMARY or UNIQUE key of the table then e.time_occurred is called "functionally dependent" and would not even throw an error in other SQL compliant DBMSs.
However, SQLite has not implemented functional dependency. In the case of SQLite (and MySQL) no error is thrown even for columns that are not functionally dependent on the GROUP BY columns.
SQLite (and MySQL) simply select a random row from the result set to fill the (in SQLite lingo) "bare column", see this.
I have a query where I join multiple tables with similar column names. To disambiguate them, I want to suffix the table name to the column name like: <column_name>_<table_name>. There are hundreds of columns in each table, so I would like to do it programmatically.
Is there a way to do something like?
sa.select([
table1.c.suffix('_1'),
table2.c.suffix('_2')]).
select_from(table1.join(table2, table1.c.id == table2.c.id))
You want to use the label keyword:
sa.select([
table1.c.column_name.label('_1'),
table2.c.column_name.label('_2')]).
select_from(table1.join(table2, table1.c.id == table2.c.id))
This will allow you to have the same column name from different tables.
If you have a table that is dynamic, or tons of columns, your best bet will be to do something like this:
pseudo code:
select * from information_schema.columns where table_name = 'my_table"
get the results from a query
return_columns = []
counter = 0
for r in results:
counter += 1
return_columns.append("`table_name`.`" + r.column_name + "` as col_{}".format(counter))
Creating dynamic sql will require you to do a bit of building out. I do this in my application all the time. Except I don't use information schema. I have a table which has my column names in it.
This should lead you in the right direction.
I am trying to get the records from my database where studentID, and lessonDate are equal to specific results. The StudentID seems to work fine, but lessonDate does not. Because of date formats, I have converted all dates to strings to be put into the database. I have set up database file so that the field is text field. I am trying to get the lesson name and rating that a student got for all exercises that they performed on a particular date. Database diagram: https://docs.google.com/drawings/d/16IqlDN2iqzVCeaUUGgeCe1R98yhUI7vQswH0tK39cAk/edit?usp=sharing
I am certain that the StudentID is correct, as I use it in other parts of this function. I am certain that the date is correct, as the 'date' variable used in the 4th line results in the correct output written to the file, and I originally had the date in the SQL query as this variable, but it didn't work. I have tried printing the lessons where this student is the foreign key, and the date is '8-10-2016'. I really have no idea why this is happening. Any suggestion or hypothesis from anyone would be greatly appreciated.
template = ("{0:50}{1:50} \n")
print(lessonDateSet)
for date in list(lessonDateSet):
target.write("Lesson date: {} \n".format(date))
target.write("\n")
target.write(template.format("Exercise name:","rating:"))
self.cur.execute("""SELECT b.roadExerciseName, a.rating
FROM lessonExercises a LEFT JOIN roadExerciseInfo b ON a.roadExerciseID=b.roadExerciseID
LEFT JOIN lessons c ON c.lessonID=a.lessonID WHERE c.StudentID = {0} AND c.lessonDate = {1}""".format(studentInfo[0][0],"8-10-2016"))
fetch = self.cur.fetchall()
print(fetch, "fetch")
'fetch' is an empty list. after this.
I have double and tripple checked my data. my data is definitely correct.
Your parameters are not being quoted correctly.
This is why you should not use string interpolation to add data into your queries. You should use the db-api's parameter substitution instead:
self.cur.execute("""SELECT b.roadExerciseName, a.rating
FROM lessonExercises a LEFT JOIN roadExerciseInfo b ON a.roadExerciseID=b.roadExerciseID
LEFT JOIN lessons c ON c.lessonID=a.lessonID
WHERE c.StudentID = ? AND c.lessonDate = ?""",
[studentInfo[0][0], "8-10-2016"])