I have been messing with Oracle DB queries that run from my JAVA app. I can successfully get them all to run in SQL Developer. But when I am trying to execute them from my JAVA app I usually get UpdatadbleResultSet Error/Exception on certain queries.
Also, sometimes I receive, ExhaustedResultset. As I mention at the bottom I will re work the question to break it down(When I get a chance). I keep editing and pretty soon it'll be a book.
Why is this? I cannot seem to pinpoint the problem.
Some queries run successfully such as:
SELECT table_name
FROM all_tables
SELECT column_name, data_length
FROM all_tab_columns
WHERE table_name = 'mytable'
But when I try and run something like
SELECT length(<myColumnName>)
FROM mytable
I get the updateableResultSetError
I am running my queries as methods called on button clicks (example below).
static void testQuery() {
String query = "SELECT blah from blah"
String length;
ResultSet rs = db.runQuery(query);
Length = rs.getString("length(myCol)")
System.out.println(length);
}
I have also tried while rs.next()
I can only think that for some reason I am unable to get into each table and I can only pull the "bigger" picture.
EDIT: Explained DB Connection
I am connecting using some other jarfiles that have been added to my project.
private static IDriver driver = null;
private static Database db = null;
I then pass in all my connection credentials in a separate method.
private void connectDB(){
driver = new OracleDriver();
db = new Database(driver)
driver.getPassword;
driver.getetc;
driver.getEtc;
}
EDIT:
When I getstacktrace all I am returning is.
Ljava.lang.StatckTraceElement;(assortment of random characters).
I may not be getting stack traces right so someone can fill me in. After all I am offering a bounty.
Also I will edit this question and break it down again when I have the time.
Your problem is that you're trying to update a query that can't be updated, hence the updateable result error. It seems that whoever is creating your database connection or executing your query is creating an updatable result set.
You can't use certain types of select in an updatable result set: you can't use aggregated functions (such as length, min, max), you can't use select * etc.)
For the full list see Result Set Limitations and Downgrade Rules
Try retrieving the value in your select statement via the columnIndex instead of the column name and see if that makes a difference.
Currently, its hard to tell what your db.runQuery() does since that code is not posted.
String query = "SELECT length(myCol) FROM myTable";
String length;
ResultSet rs = db.runQuery(query);
while (rs.next()) {
length = rs.getString(1);
System.out.println(length);
}
I've got an inkling what may be happening here (which would explain why some queries work, and some don't). Accoring to the jdbc ResultSet javadocs, when using the getString() method of the result set, the column label.
the label for the column specified with the SQL AS clause.
If the SQL AS clause was not specified, then the label is the name of the column
As "length(myCol)" is neither a label nor a column name, it may be that it fell over because of that (but without stacktrace it is difficult to say what your problem actually is).
Try
String query = "SELECT length(myCol) AS myCol_len FROM myTable"
ResultSet rs = db.runQuery(query);
String length = rs.getString("myCol_len");
Though are you sure, you didn't want:
int length = rs.getInt("myCol_len");
Alternatively (as written by Kal), you can use the column index to get the data from the result set, which oblivates the need for a SQL AS label:
String query = "SELECT length(myCol) FROM myTable"
ResultSet rs = db.runQuery(query);
String length = rs.getString(1);
Related
I have read the previous questions but none of them seem to match my problem although they might seem similar at first. :/_
So, I am working on a local database on Java(JDBC). When I press a button I should be getting the result of a "SELECT" query. So far so good, but for some reason which my beginner brain does not understand I keep getting only one row from the query. I have even run the same exact query on "DB Browser for SQLite" and it returns the correct result (1+ rows) .
So this is the method I am using to get the result of the query:
public ResultSet returnBill(int no) throws SQLException{
String sql = "SELECT * FROM billList WHERE no = " + no + " ;";
ResultSet thisSet = stmt.executeQuery(sql); // stmt is a 'Statement' type variable
return thisSet;
}
The method does not crash but it only returns the very first row of a query which should return more than 2 ( while (thisSet.next()) RUNS ONCE). I run other "SELECT" queries on the program which are supposed to return more than one rows and they all work fine so it's not a matter of not being able to start/close the connection etc.
Below is the method being used:
int number = table.getModel().getValueAt(rows, 0);
ResultSet thisSet = db.returnBill(number);
while (thisSet.next()){
String name = thisSet.getString("name");
int quantity = thisSet.getInt("quantity");
// do something with the returned data
}
So I get this magical number from a table (of course I made sure it's not 0, -1 etc.) and I run a query using that number. You could think of the structure of the table consisting of columns :
number | name | quantity |
where 'number' is nonzero.
I understand that probably using this method to run a query on a DB might not be safe or might post security threats but it's not the case right now. I have been working on this project for quite a long time already and I have been through many silly mistakes and I think this is yet one of them. Any help is APPRECIATED ! :D
So yes, it was a silly mistake as I expected.
So I had previously initiated a variable
Database db = new Database();
which opened the database for 2 queries (the SELECT query and an UPDATE query on another table as shown) which would then be closed at the end of the following code.
When I removed this UPDATE query however the loop executed the correct amount of times. So it seems like the SQLite JDBC is somehow prone to running a SELECT and UPDATE query on the same Statement (as far as my super mega* brain perceives it.)
So I created 2 connections at the very beginning and closed them at the end using one of them for the SELECT and the other one for the UPDATE query:
Database db = new Database(); // open database
Database db2 = new Database(); // open another creepy one :/
int number = table.getModel().getValueAt(rows, 0);
ResultSet thisSet = db.returnBill(number);
while (thisSet.next()){
String name = thisSet.getString("name");
int quantity = thisSet.getInt("quantity");
// do something with the returned data
// --------> STUPIDO <----------
//** Now executing an UPDATE query on db2 :
// ex.: UPDATE anotherTable SET amount = (current+ "+ quantity+") WHERE name= '" + name+ "' ;";
}
db.closeConn(); // close db ++
db2.closeConn(); // closes db 2
I don't know if this is the best approach but it solved my problem, so I'm leaving it so probably it could help. Any suggestions though would be welcomed :D
I am using JDBC PreparedStatement to query a Teradata database from a web service. My table has a PHONE_NUMBER column, stored as VARCHAR(10). I have always used PreparedStatement setString() to supply the parameter for this column, like below:
String myPhoneNumber = "5551234567";
String sql = "SELECT * FROM MYTABLE " +
"WHERE PHONE_NUMBER = ? ";
PreparedStatement p_stmt = db.getPreparedStatement(sql);
p_stmt.setString(1, myPhoneNumber);
ResultSet rs = db.executeQuery(p_stmt);
It returns correct results, but I noticed the CPU Teradata is using for this query is quite high. According to the EXPLAIN plan, it appears that Teradata is interpreting the myPhoneNumber parameter as a FLOAT, instead of VARCHAR, and so it has to do a data conversion to compare it to the VARCHAR column PHONE_NUMBER. Here is an excerpt of the EXPLAIN plan:
...
MYDATABASE.MYTABLE.PHONE_NUMBER (FLOAT, FORMAT
'-9.99999999999999E-999'))= 5.55123456700000E 009)
So, I came up with the below, which showed a great improvement in CPU usage (99.86% improvement):
String myPhoneNumber = "5551234567";
String sql = "SELECT * FROM MYTABLE " +
"WHERE PHONE_NUMBER = ''||?||'' ";
PreparedStatement p_stmt = db.getPreparedStatement(sql);
p_stmt.setString(1, myPhoneNumber);
ResultSet rs = db.executeQuery(p_stmt);
So my question is why is this necessary? Shouldn't setString tell JDBC to tell Teradata to expect a String/VARCHAR parameter?
Thanks!
Have you tried String myPhoneNumber = "'5551234567'";
Note -- The inclusion of the single quotes to wrap the value.
If you look at the example in the Teradata manuals here, you will see that a Query Band being set the same way as the OP's first example arrives as expected without single quotes wrapping it. It would seem to me this behavior in the first example of the OP is expected.
EDIT
The sample code provided by Teradata for their JDBC driver is using java.sql.PreparedStatment. With this their example program uses setString without any tricks to provide a string value for an INSERT statement. Sample Code If you are not able to replicate that behavior, I would open an incident with the Teradata GSC.
This question already has answers here:
Java PreparedStatement complaining about SQL syntax on execute()
(6 answers)
Closed 6 years ago.
This is a really weird error that only started appearing today. When I use a prepared statement with ? for parameters, I get an error, but when I use it without parameters, it works just fine.
Here is the error-causing code:
String table = "files";
Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
PreparedStatement prep = conn.prepareStatement("SELECT * FROM ?");
prep.setString(1, table);
ResultSet rs = prep.executeQuery();
while(rs.next()) {
System.out.println(rs.getString("file_name"));
}
This produces the following error:
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''files'' at line 1
Also, changing it to the following works just fine:
String table = "files";
Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
PreparedStatement prep = conn.prepareStatement("SELECT * FROM " + table);
ResultSet rs = prep.executeQuery();
while(rs.next()) {
System.out.println(rs.getString("file_name"));
}
This doesn't seem to be making a whole lot of sense. Any ideas?
Tried it on another table and got more weired results.
This works and logs the admin in correctly:
String sql = "SELECT * FROM " + ADMIN_AUTH_TABLE + " WHERE " + column + " = '" + hashedPassword + "'";
PreparedStatement prepared = connection.prepareStatement(sql);
The following doesn't cause errors, but returns a message saying that the password entered is incorrect (it's correct - I double triple checked).
String sql = "SELECT * FROM " + ADMIN_AUTH_TABLE + " WHERE ? = ?";
PreparedStatement prepared = connection.prepareStatement(sql);
prepared.setString(1, column);
prepared.setString(2, hashedPassword);
Got it: use ? for values.
Also, the answer here helped.
Bind parameters cannot be used for identifiers in the SQL statement. Only values can supplied through bind placeholders.
This will work:
SELECT foo FROM bar WHERE id = ?
This will not work, because the table name is an identifier
SELECT foo FROM ? WHERE id = 2
You can't supply a column name, because column names are also identifiers.
A statement like this will run, but it may not do what you think it does.
SELECT ? AS foo FROM bar WHERE ? = 0
If we supply values of 'foo' for both placeholders, the query will actually be equivalent to a query containing two string literals:
SELECT 'foo' AS foo FROM bar WHERE 'foo' = 0
MySQL will run that statement, because it's a valid statement (if the table bar exists and we have privileges on it.) That query will return every row in bar (because the predicate in the WHERE clause evaluates to TRUE, independent of the contents of the table.. And we get returned the constant string foo.
It doesn't matter one whit that the string foo happens to match the name of column in our table.
This restriction has to do with how the SQL optimizer operates. We don't need to delve into all the details of the steps (briefly: parsing tokens, performing syntax check, performing semantics check, determining query plan, and then the actual execution of the query plan.)
So here's the short story: The values for bind parameters are supplied too late in that process. They are not supplied until that final step, the execution of the query plan.
The optimizer needs to know which tables and columns are being referenced at earlier stages... for the semantics check, and for developing a query plan. The tables and columns have to be identified to the optimizer. Bind placeholders are "unknowns" at the time the table names and column names are needed.
(That short story isn't entirely accurate; don't take all of that as gospel. But it does explain the reason that bind parameters can't be used for identifiers, like table names and column names.)
tl;dr
Given the particular statement you're running, the only value that can be passed in as a bind parameter would be the "hashedPassword" value. Everything else in that statement has to be in the SQL string.
For example, something like this would work:
String sqltext = "SELECT * FROM mytable WHERE mycolumn = ?";
PreparedStatement prepared = connection.prepareStatement(sqltext);
prepared.setString(1, hashedPassword);
To make other parts of the SQL statement "dynamic" (like the table name and column name) you'd have to handle that in the Java code (using string concatenation.) The contents of that string would need to end up like the contents of the sqltext string (in my example) when it's passed to the prepareStatement method.
The parameters of PreparedStatement should be applied only in parameters that can be used in conditional clauses. The table name is not the case here.
If you have a select where the table name can be applied in the conditional clause you can do it, otherwise you can not.
I am trying to insert into a db that I have, and I'd like to do so through parameters. I am connecting to a postgres db using java.
I can connect to the db just fine. I know that because I have various operations that I am using that are already working were I can see, and update existing rows in my db. I am having trouble with INSERT.
I have the following:
private String _update_rentals = "INSERT into rentals (cid, mid) values (?,?)";
private PreparedStatement _update_rentals_statement;
private String _update_movie_status = "UPDATE Movie SET checkedout = true WHERE mid = ?";
private PreparedStatement _update_movie_status_statement;
And I initialize them:
_update_movie_status_statement = _customer_db.prepareStatement(_update_movie_status);
_update_rentals_statement = _customer_db.prepareStatement(_update_rentals);
And
while (movieAvail.next()){
System.out.println(movieAvail.getBoolean(1));
if (movieAvail.getBoolean(1) == false){
//Do chekcout
_update_rentals_statement.clearParameters();
_update_rentals_statement.setInt(1, cid);
_update_rentals_statement.setInt(2, mid);
_update_rentals_statement.executeQuery();
_update_movie_status_statement.clearParameters();
_update_movie_status_statement.setInt(1, mid);
_update_movie_status_statement.executeQuery();
System.out.println("Enjoy your movie!");
}
}
I am getting an error with both of the executeQuery() calls. For some reason I am getting the following error with both:
Exception in thread "main" org.postgresql.util.PSQLException: No results were returned by the query.
I looked at other posts, and I believed that I was following syntax for both insert/ update correctly, so maybe I am overlooking some aspect of this.
This is all part of a larger code base, so I did not want to include the methods these pieces of code are in. But these are the isolated instances which play a part with this code.
In general, when you execute a query, you are willing to retrieve some kind of information from the database. This is usually the case when you are executing SELECT queries. However, with INSERT and UPDATE statements, you are not querying the database, you are simply executing an update or inserting new rows. In the documentation of PreparedStatement you can see in which cases an exception is being thrown when you try to call executeQuery:
Throws: SQLException - if a database access error occurs; this method
is called on a closed PreparedStatement or the SQL statement does not
return a ResultSet object
So in your case the problem is that your statements do not return a ResultSet. You should use execute or executeUpdate instead. The former simply executes the update, while the latter does the same, but also returns the number of affected rows.
I think the main issue is that you are calling executeQuery(), which expects a result to be returned, but Insert/Update are not queries and don't return a result. Try just calling execute().
I have a table PERSON with more than 5 millions rows and I need to update field NICKNAME on each one of them based on the field NAME inside the same table.
ResultSet rs = statement.executeQuery("select NAME from PERSON");
while(rs.next())
{
// some parsing function like:
// Nickname = myparsingfunction(rs.getString("NAME"));
rs.updateString( "NICKNAME", Nickname );
rs.updateRow();
}
But I got this error:
not implemented by SQLite JDBC driver
I'm using sqlite-jdbc-3.8.11.2.jar downloaded at https://bitbucket.org/xerial/sqlite-jdbc/downloads.
I know I could use the following SQL query:
statement.executeUpdate("update PERSONS set NICKNAME = Nickname where ID = Id");
But that would take forever and I understand updating ResultSet would be faster. So what options do I have to update the table on the fastest way? Any other driver available? Should I move out of Java?
UPDATE
I was able to find a fast solution using below syntax. The block between CASE and END was a concatenated string that I built before executing the SQL query, so I could send all updates at once.
update PERSON
set NICKNAME= case ID
when 173567 then 'blabla'
when 173568 then 'bleble'
...
when 173569 then 'blublu'
end
where ID in (173567, 173568, 173569)
As you have encountered, the SQLite JDBC driver does not currently support the updateString operation. This can be seen in the source code for this driver.
I can think of three options:
As you stated in your question, you can select the name and ID of the person and then update the person by its ID. Those updates could be done in a batch (using PreparedStatement.addBatch()) to improve performance (tutorial).
Implement the method myparsingfunction in pure SQL so that the query could become UPDATE PERSONS SET NICKNAME = some_function(NAME).
Create an user-defined function (using org.sqlite.Function), implemented in Java, and call it inside the SQL. Example, taken from this answer:
Function.create(db.getConnection(), "getNickName", new Function() {
protected void xFunc() throws SQLException {
String name = value_text(0);
String nickName = ...; // implement myparsingfunction here
result(nickName);
}
});
and use it like this: UPDATE PERSONS SET NICKNAME = getNickName(NAME);
SQLite does not support stored procedures so that option is out of the table.
I'm not sure which of these options would provide the best performance (certainly using pure SQL would be faster but that may not be a viable solution). You should benchmark each solution to find the one that fits you.