I would like to leverage a prepared statement in order to insert/update postgres db.
The thing is that the table contains nullable columns I might or might not have a value for in runtime.
In case I do not have anything to insert/update , I need the old value to remain.
Is there a syntax I can use for that case?
What are the best practices in cases like that, it seems like a very common problem?
P.S
I use java/scala with plain jdbc.
You could, of course, just construct a statement without the columns you don't want to update.
If this isn't an option in your usecase, you could construct a case expression with a "flag" to indicate if the column should be bound or not.
E.g.:
String sql = "UPDATE mytable " +
"SET " +
"col1 = CASE(? WHEN 1 THEN ? ELSE col1 END), " +
"col2 = CASE(? WHEN 1 THEN ? ELSE col2 END) " +
"WHERE id = ?";
PreparedStatement ps = con.prepareStatement(sql);
// col1 should be updated
ps.setInt(1, 1);
ps.setString(2, newValueCol1);
// col2 should not be updated
ps.setInt(3, 0);
ps.setString(4, null); // or any other value...
// bind the where clause
ps.setInt(5, someId);
ps.executeUpdate();
// close resources and clean up, omitted for brevity's sake
Related
I'm working in one quiz game. There is question maker window. Which works good for saving question. But when want update one of text Field and press save, than error is happening. something is wrong with syntax?!
void insertCell(String tableNamer, String column, String value, int id) throws ClassNotFoundException, SQLException{
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:file:C:/Users/Juris Puneiko/IdeaProjects/for_my_testings/src/sample/DB/Questions/For_Private/Easy", "Juris", "1");
PreparedStatement ps = conn.prepareStatement("UPDATE ? SET ? = ? where ID = ?");
ps.setString(1, tableNamer);
ps.setString(2, column);
ps.setString(3, value);
ps.setInt(4, id);
ps.executeUpdate();
ps.close();
conn.close();
}
org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "UPDATE ?[*] SET ? = ? WHERE ID = ? "; expected "identifier"; SQL statement:
UPDATE ? SET ? = ? where ID = ? [42001-196]
What is this >>> [*]?
What does it mean?
String sql = "UPDATE " + tableNamer + " SET " + column + " = ? where ID = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, value);
ps.setInt(2, id);
ps.executeUpdate();
ps.close();
conn.close();
The placeholders can only be used for values in most SQL databases, not for identifiers like table or column names:
"UPDATE myTable SET myCol = ? where ID = ?" -- OK
"UPDATE ? SET ? = ? where ID = ?" -- not OK
The reason is that those parameters are also used for prepared statements, where you send the query to the database once, the database "prepares" the statement, and then you can use this prepared statement many times with different value parameters. this can improve DB performance because DB can compile and optimize the query and then use this processed form repeatedly - but to be able to do this, it needs to know names of the tables and columns involved.
To fix this, you only leave the ?s in for the values, and you concatenate the tableNamer and column manually:
"UPDATE " + tableNamer + " SET " + column + " = ? where ID = ?"
Keep in mind though that by doing this, tableNamer and column are now potentially vulnerable to SQL injection. Make sure that you don't allow user to provide or affect them, or else sanitize the user input.
Im trying to add the number 1 to a certain field. How could i manage to do that? Ive tried it but i can never get it to add 1. My ms access table column is set to Number not text.
if (s2.equals(box1Text)) {
if (s3.equals(box2Text)) {
if (s5.equals(currentWinner)) {
String sql = "UPDATE Table2 "+ "SET Score = ? " + "WHERE Better = '" + s1+"'";
PreparedStatement stmt = con.prepareStatement(sql);
//points made here
if (s4.equals(betScore)) {
stmt.setString(1, "+1");//how could i add 1 to the field?
stmt.executeUpdate();
} else {
}
First you do something that is regarded as bad practice : you construct your query by adding the value of a parameter in the string.
String sql = "UPDATE... >+ s1 +<..."
Please nether do that (what is between > and <) when programming seriouly, but allways use ? to pass values.
Second, SQL can do the job for you :
String sql = "UPDATE Table2 SET Score = Score + 1 WHERE Better = ?";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, s1);
stmt.executeUpdate();
(try, catch, tests and other details omitted for brevity)
I have a requirement where I need to insert mobile number in mysql if and only if the number is is not present.So for this I am first checking if a number is present in mysql using select query .If number is not present then insert.Following is my code
PreparedStatement pt1=con.prepareStatement("select * from registerSmsUsers where mobile='"+mobile+"'");
PreparedStatement pt=con.prepareStatement("insert into registerSmsUsers values(?,?,?)");
pt.setString(1, name);
pt.setString(2, email);
pt.setString(3, mobile);
ResultSet rs1=pt1.executeQuery();
if(rs1.next())
{pt.executeUpdate();}
i dont know whether this is a efficient way or not.Please suggest me a better way then this
Probably the easiest way in mysql is:
insert ignore into registerSmsUsers values(?,?,?)
When assuming you have unique key on mobile
You may check it here: How to 'insert if not exists' in MySQL?
Or here: http://dev.mysql.com/doc/refman/5.6/en/insert.html
Many of the proposed solutions (including yours) have a race condition that can cause a primary key or unique constraint violation. You code also have a possible SQL injection attack by concatenating SQL rather than using prepared statement parameters. Use SELECT...FOR UPDATE.
PreparedStatement ps = con.prepareStatement("SELECT name, email, mobile FROM registerSmsUsers WHERE mobile=? FOR UPDATE",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
ps.setString(1, mobile);
ResultSet rs = ps.executeQuery();
if (rs.next()) { // it exists already
rs.moveToCurrentRow();
rs.updateString(3, mobile);
rs.updateRow();
} else { // it does NOT exist
rs.moveToInsertRow();
rs.updateString(1, name);
rs.updateString(2, email);
rs.updateString(3, mobile);
rs.insertRow();
}
rs.close();
ps.close();
EDIT: Just make sure you have an index on registerSmsUsers.
CREATE INDEX registerSmsUsers_mobile_ndx ON registerSmsUsers(mobile)
or a unique contraint (which implicitly creates an index):
ALTER TABLE registerSmsUsers ADD CONSTRAINT registerSmsUsers_mobile_unq UNIQUE (mobile)
With an index, even with millions of records the update/insert will basically be instant.
EDIT2: Added cursor/result set options.
I think it would be better to create a stored procedure and then in that stored procedure you can first use the IF NOT EXISTS clause to check if the user exists using the select statement. If the user is not present you can insert the user in database.
Something like this:
IF NOT EXISTS(SELECT 1 FROM `registerSmsUsers` WHERE mobile= #mobile) THEN
BEGIN
INSERT INTO
`registerSmsUsers`
(
//column names
)
VALUES
(
//values
);
END;
END IF;
Also there is a INSERT IGNORE statement which you can use like this:
insert ignore into registerSmsUsers values(?,?,?)
if not exists(select * from registerSmsUsers where mobile='232323') <-- will check your mobile no
begin
insert into registerSmsUsers values(?,?,?)
end
This one is also an efficient way to check your method is also working fine but this also can be done
See difference is you will have only one query here
i hope this will help you thanks
[Edit]
Your questions answer
Ya there is a execution time diff between yours and mine query its depends upon a database size what you are using if you are using small size database (probably 1000 people) then you will not see any diff between your query and mine query but if your are using lakhs of users then your will have a performace issues check include execution plan in mysql you will get realtime difference between two
As requested, here is my tweaked version of brettw's answer:
import java.sql.*;
public class MySQLtest {
public static void main(String[] args) {
Connection con;
try {
con = DriverManager.getConnection(
"jdbc:mysql://192.168.1.3/zzzTest?" +
"useUnicode=yes&characterEncoding=UTF-8" +
"&user=root&password=whatever");
String newName = "Gord";
String newEmail = "gord#example.com";
String newMobile = "416-555-1212";
String sql =
"SELECT " +
"id, " +
"name, " +
"email, " +
"mobile " +
"FROM registerSmsUsers " +
"WHERE mobile = ? " +
"FOR UPDATE";
PreparedStatement pst = con.prepareStatement(
sql,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
pst.setString(1, newMobile);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
rs.moveToCurrentRow();
rs.updateString("name", newName);
rs.updateString("email", newEmail);
rs.updateRow();
System.out.println("Existing row updated.");
}
else {
rs.moveToInsertRow();
rs.updateString("name", newName);
rs.updateString("email", newEmail);
rs.updateString("mobile", newMobile);
rs.insertRow();
System.out.println("New row inserted.");
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
Note that id is the Primary Key for the table: int(11) NOT NULL AUTO_INCREMENT
How can I use a prepared statement to delete entries from a database? I have found that I must write the following code
String deleteSQL = "DELETE DBUSER WHERE USER_ID = ?
but I want to specify a clause with more than one variable. I have used the AND operator but it doesn't seem to work.
Here is an example if your syntax is not correct..
DELETE DBUSER WHERE USER_ID = ? and USER_NAME = ?;
you can append more conditions in where clause by using more AND ... operators.
OR if you have more than one USER_IDs to delete in a single query..
DELETE DBUSER WHERE USER_ID in (?, ?, ?, ?);
It's must work/ for example
Select from Employee e where e.ID < ? and e.ID >= ? order by e.ID
to set values use this:
int id1 = 1;
int id2 = 10;
preparedStatement.setInt(2, id1);
preparedStatement.setInt(1, id2);
for delete I use this code:
public synchronized boolean deleteNewsById(Integer[] idList)
throws NewsManagerException {
DatabaseConnection connection = pool.getConnection();
StringBuffer buffer = new StringBuffer();
buffer.append("(");
buffer.append(idList[0]);
for (int i = 1; i < idList.length; i++) {
buffer.append(",");
buffer.append(idList[i]);
}
buffer.append(")");
PreparedStatement statement = connection
.getPreparedStatement(DELETE_NEWS_BY_ID + buffer);
}
and sql query looks like this
private static final String DELETE_NEWS_BY_ID = "delete from NEWS where ID in ";
or simple write delete from NEWS where ID in (?,?,?) and set values like in first example
I think the response from Aleksei Bulgak is correct, but to perhaps more straightforwardly word it...you can set your parameters like this:
String stmt = "DELETE DBUSER WHERE USER_ID = ? and (USER_NAME = ? or USER_NAME = ?)";
preparedStatement.setInt(1, firstParam);
preparedStatement.setString(2, secondParam);
preparedStatement.setString(3, thirdParam);
...and for however many parameters(question marks) in your SQL (no matter if you're using IN or whatever you want), you should set that many parameters here(using setInt for ints, setString for Strings, etc). This goes for select and delete queries.
Are you looking for the IN operator which allows you to specify multiple values in the WHERE clause such as in my example.
String deleteSQL = "DELETE DBUSER WHERE USER_ID IN (?)"
Though in PreparedStatement IN clause alternatives there are some useful answers and links that you may want to take a look at such as Batch Statements in JDBC which discuss the pros and cons of different batching approaches. The IN approach I'm suggesting is part of that discussion. The end result is that you make just one trip to the database, rather than one per delete and that's better performing because of the reduced network activity required.
Consider this simple method:
public ResultSet getByOwnerId(final Connection connection, final Integer id) throws SQLException {
PreparedStatement statement = connection.prepareStatement("SELECT * FROM MyTable WHERE MyColumn = ?");
statement.setObject(1, id);
return statement.executeQuery();
}
The example method is supposed to select everything from some table where a column's value matches, which should be simple.
The ugly detail is that passing NULL for the id will result in an empty ResultSet, regardless how many rows there are in the DB because SQL defines NULL as not euqaling anyting, not even NULL.
The only way to select those rows I know of is using a different where clause:
SELECT * FROM MyTable WHERE MyColumn IS NULL
Unfortunately the only way to make the method work properly in all cases seems to be to have two entirely different execution pathes, one for the case where the id argument is null and one for regular values.
This is very ugly and when you have more than one column that can be nulled can get very messy quickly. How can one handle NULL and normal values with the same code path/statement?
I haven't tried this, but the way to send nulls to preparedStatements is to use the setNull() option
PreparedStatement statement = connection.prepareStatement("SELECT * FROM MyTable WHERE MyColumn = ?");
if ( id == null)
statement.setNull(1, java.sql.Types.INTEGER);
else
statement.setObject(1, id);
return statement.executeQuery();
EDIT : Thanks for the responses #Gabe and #Vache. It does not look like there is an easy way to do this other than taking a more longwinded approach to dynamically create the preparedstatment.
Something like this should work --
String sql = " SELECT * FROM MyTable WHERE " + (id==null)?"MyColumn is null":"MyColumn = ?" ;
PreparedStatement statement = connection.prepareStatement(sql);
if ( id != null)
statement.setObject(1, id);
return statement.executeQuery();
You could use a query like this:
select * from MyTable where
case when ?1 is null then MyColumn is null else MyColumn = ?1
But it reuses the same parameter, so I don't know whether the syntax I've proposed (?1) will work.