I have following String like that:String sql = "SELECT COL1, COL2, COL3 FROM SCHEMA.TABLE";
Now I need to get this value from String: SCHEMA.TABLE and this value is always changing. And also entire SQL is always changing.
How would I achieve this?
Don't know how to manipulate with different values?
This value is always after FROM and SQL may also have other values after FROM(ORDER BY, GROUP BY...)
SAMPLE SQL:
`SELECT ZGAR.ZGAR_ID, JARTI.ARTI_NAME, JZGAR.ZGAR_NAME, ZGAR.KAZG_ID, ZGAR.KZPO_ID, JZGAR.ZGAR_DESC FROM NETZGP.ZGANJEARTIKEL ZGAR LEFT JOIN NETZGP.J_ARTI JZGAR ON ZGAR.ZGAR_ID = JZGAR.ZGAR_ID LEFT JOIN NETZGP.J_ARTIKEL JARTI ON ZGAR.ARTI_ID = JARTI.ARTI_ID AND JZGAR.JEZI_ID = JARTI.JEZI_ID WHERE ZGAR.ARTI_ID = 1 AND JZGAR.JEZI_ID = 1 WITH UR`
The most succinct and reliable way is via regex:
String tableName = sql.replaceAll(".*FROM (\\S+).*", "$1");
This will also work when there's a WHERE clause after the table name.
Try with substring().
String result= queryString.substring(queryString.lastIndexOf(" ")+1);
gives the last part of the query.
If atleast FROM is there in every query
String[] result= s.split("FROM");
System.out.println(result[1]);
Use:String table= queryString.substring(queryString.lastIndexOf(" ")+1);
See String#substring().
If your sql query is changing frequently, you should use PreparedStatement instead of creating Statement many times.
Connection con = DriverManager.getConnection(url, user, password);
PreparedStatement pst = con.prepareStatement("SELECT COL1, COL2, COL3 FROM ?");
String schemaTable = "<ANY_TEXT>";
p.setString(1, schemaTable);
ResultSet rs = p.executeQuery();
After that you can just change the schemaTable variable and again do p.setString(1, schemaTable); rs = p.executeQuery(); to get new results.
Related
I'm trying to use prepared statements to set a table name to select data from, but I keep getting an error when I execute the query.
The error and sample code is displayed below.
[Microsoft][ODBC Microsoft Access Driver] Parameter 'Pa_RaM000' specified where a table name is required.
private String query1 = "SELECT plantID, edrman, plant, vaxnode FROM [?]"; //?=date
public Execute(String reportDate){
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn = DriverManager.getConnection(Display.DB_MERC);
PreparedStatement st = conn.prepareStatement(query1);
st.setString(1, reportDate);
ResultSet rs = st.executeQuery();
Any thoughts on what might be causing this?
A table name can't be used as a parameter. It must be hard coded. So you can do something like:
private String query1 = "SELECT plantID, edrman, plant, vaxnode FROM [" + reportDate + "?]";
If you need a solution which is not vulnerable to SQL injection, you have to duplicate the query for all tables you need:
final static String QUERIES = {
"SELECT x FROM Table1 x WHERE a=:a AND b=:b AND ...",
"SELECT x FROM Table2 x WHERE a=:a AND b=:b AND ...",
"SELECT x FROM Table3 x WHERE a=:a AND b=:b AND ...",
...
};
And yes: the queries are duplicates and only the table name differs.
Now you simply select the query that fits your table, e.g. like
...
PreparedStatement st = conn.prepareStatement(QUERIES[index]);
...
You can use this approach wich JPA, Hibernate, whatever...
If you want a more verbose approach consider using an enum like
enum AQuery {
Table1("SELECT x FROM Table1 x WHERE a=:a AND b=:b AND ..."),
Table2("SELECT x FROM Table2 x WHERE a=:a AND b=:b AND ..."),
Table3("SELECT x FROM Table3 x WHERE a=:a AND b=:b AND ..."),
...
private final String query;
AQuery(final String query) {
this.query = query;
}
public String getQuery() {
return query;
}
}
Now use the either an index
String sql = AQuery.values()[index].getQuery();
PreparedStatement st = conn.prepareStatement(sql);
...
Or use a table name
String sql = AQuery.valueOf("Table1").getQuery();
PreparedStatement st = conn.prepareStatement(sql);
...
This is technically possible with a workaround, but very bad practice.
String sql = "IF ? = 99\n";
sql += "SELECT * FROM first_table\n";
sql += "ELSE\n";
sql += "SELECT * FROM second_table";
PreparedStatement ps = con.prepareStatement(sql);
And then when you want to select from first_table you set the parameter with
ps.setInt(1, 99);
Or if not, you set it to something else.
As a number of people have said, you can't use a statement parameter for a table name, only for variables as part of the condition.
Based on the fact you have a variable table name with (at least) two table names, perhaps it would be best to create a method which takes the entity you are storing and returns a prepared statement.
PreparedStatement p = createStatement(table);
You can't set table name in prepared statement
As said before, it is not possible to set the table name in a prepared statement with preparedStatement.setString(1, tableName). And it is also not possible to add parts of the SQL query to a prepared statement (eg preparedStatement.addSql(" or xyz is null")).
How to do it right without risking SQL injections?
The table name must be inserted into the SQL (or JQL) query you want to execute with string operations like "select * from " + tableName or String.format("select * from %s", tableName)
But how to avoid SQL injections?
If the table name does not come from user input, you are probably safe.
For example, if you make a decision like here
String tableName;
if(condition) {
tableName = "animal";
} else {
tableName = "plant";
}
final String sqlQuery = "delete from " + tableName;
...
If the table name depends on the users input, you need to check the input manually.
For example, with a white-list containing all valid table names:
if(!tableNamesWhitelist.contains(tableName)) {
throw new IllegalArgumentException(tableName + " is not a valid table name");
}
String sqlQuery = "delete from " + tableName;
or with an enum:
public enum Table {
ANIMAL("animal"),
PLANT("plant");
private sqlTableName;
private TableName(String sqlTableName) {
this.sqlTableName= sqlTableName;
}
public getSqlTableName() {
return sqlTableName;
}
}
and then convert the user-input string like ANIMAL into Table.ANIMAL. An exception is thrown, if no fitting enumeration value does exist.
eg
#DeleteMapping("/{table}")
public String deleteByEnum(#PathVariable("table") Table table) {
final String sqlQuery = "delete from " + table.getSqlTableName();
...
}
Of course these examples work with select, update, ... too and a lot of other implementations to check the user input are possible.
This might help:
public ResultSet getSomething(String tableName) {
PreparedStatement ps = conn.prepareStatement("select * from \`"+tableName+"\`");
ResultSet rs = ps.executeQuery();
}
I'm not sure you can use a PreparedStatement to specify the name of the table, just the value of some fields. Anyway, you could try the same query but, without the brackets:
"SELECT plantID, edrman, plant, vaxnode FROM ?"
String table="pass";
String st="select * from " + table + " ";
PreparedStatement ps=con.prepareStatement(st);
ResultSet rs = ps.executeQuery();
I'm trying to use prepared statements to set a table name to select data from, but I keep getting an error when I execute the query.
The error and sample code is displayed below.
[Microsoft][ODBC Microsoft Access Driver] Parameter 'Pa_RaM000' specified where a table name is required.
private String query1 = "SELECT plantID, edrman, plant, vaxnode FROM [?]"; //?=date
public Execute(String reportDate){
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn = DriverManager.getConnection(Display.DB_MERC);
PreparedStatement st = conn.prepareStatement(query1);
st.setString(1, reportDate);
ResultSet rs = st.executeQuery();
Any thoughts on what might be causing this?
A table name can't be used as a parameter. It must be hard coded. So you can do something like:
private String query1 = "SELECT plantID, edrman, plant, vaxnode FROM [" + reportDate + "?]";
If you need a solution which is not vulnerable to SQL injection, you have to duplicate the query for all tables you need:
final static String QUERIES = {
"SELECT x FROM Table1 x WHERE a=:a AND b=:b AND ...",
"SELECT x FROM Table2 x WHERE a=:a AND b=:b AND ...",
"SELECT x FROM Table3 x WHERE a=:a AND b=:b AND ...",
...
};
And yes: the queries are duplicates and only the table name differs.
Now you simply select the query that fits your table, e.g. like
...
PreparedStatement st = conn.prepareStatement(QUERIES[index]);
...
You can use this approach wich JPA, Hibernate, whatever...
If you want a more verbose approach consider using an enum like
enum AQuery {
Table1("SELECT x FROM Table1 x WHERE a=:a AND b=:b AND ..."),
Table2("SELECT x FROM Table2 x WHERE a=:a AND b=:b AND ..."),
Table3("SELECT x FROM Table3 x WHERE a=:a AND b=:b AND ..."),
...
private final String query;
AQuery(final String query) {
this.query = query;
}
public String getQuery() {
return query;
}
}
Now use the either an index
String sql = AQuery.values()[index].getQuery();
PreparedStatement st = conn.prepareStatement(sql);
...
Or use a table name
String sql = AQuery.valueOf("Table1").getQuery();
PreparedStatement st = conn.prepareStatement(sql);
...
This is technically possible with a workaround, but very bad practice.
String sql = "IF ? = 99\n";
sql += "SELECT * FROM first_table\n";
sql += "ELSE\n";
sql += "SELECT * FROM second_table";
PreparedStatement ps = con.prepareStatement(sql);
And then when you want to select from first_table you set the parameter with
ps.setInt(1, 99);
Or if not, you set it to something else.
As a number of people have said, you can't use a statement parameter for a table name, only for variables as part of the condition.
Based on the fact you have a variable table name with (at least) two table names, perhaps it would be best to create a method which takes the entity you are storing and returns a prepared statement.
PreparedStatement p = createStatement(table);
You can't set table name in prepared statement
As said before, it is not possible to set the table name in a prepared statement with preparedStatement.setString(1, tableName). And it is also not possible to add parts of the SQL query to a prepared statement (eg preparedStatement.addSql(" or xyz is null")).
How to do it right without risking SQL injections?
The table name must be inserted into the SQL (or JQL) query you want to execute with string operations like "select * from " + tableName or String.format("select * from %s", tableName)
But how to avoid SQL injections?
If the table name does not come from user input, you are probably safe.
For example, if you make a decision like here
String tableName;
if(condition) {
tableName = "animal";
} else {
tableName = "plant";
}
final String sqlQuery = "delete from " + tableName;
...
If the table name depends on the users input, you need to check the input manually.
For example, with a white-list containing all valid table names:
if(!tableNamesWhitelist.contains(tableName)) {
throw new IllegalArgumentException(tableName + " is not a valid table name");
}
String sqlQuery = "delete from " + tableName;
or with an enum:
public enum Table {
ANIMAL("animal"),
PLANT("plant");
private sqlTableName;
private TableName(String sqlTableName) {
this.sqlTableName= sqlTableName;
}
public getSqlTableName() {
return sqlTableName;
}
}
and then convert the user-input string like ANIMAL into Table.ANIMAL. An exception is thrown, if no fitting enumeration value does exist.
eg
#DeleteMapping("/{table}")
public String deleteByEnum(#PathVariable("table") Table table) {
final String sqlQuery = "delete from " + table.getSqlTableName();
...
}
Of course these examples work with select, update, ... too and a lot of other implementations to check the user input are possible.
This might help:
public ResultSet getSomething(String tableName) {
PreparedStatement ps = conn.prepareStatement("select * from \`"+tableName+"\`");
ResultSet rs = ps.executeQuery();
}
I'm not sure you can use a PreparedStatement to specify the name of the table, just the value of some fields. Anyway, you could try the same query but, without the brackets:
"SELECT plantID, edrman, plant, vaxnode FROM ?"
String table="pass";
String st="select * from " + table + " ";
PreparedStatement ps=con.prepareStatement(st);
ResultSet rs = ps.executeQuery();
I have a first resultset within which I have to iterate through userids and for each userid, I have to perform several select count(*)'s all of which return single valued outputs. If you haven't understood what I said, please follow the pseudo code below:
ResultSet rs = stmt.executeQuery("select userid from tablename");
while(rs.next()){
String userid = rs.getString("userId");
ResultSet rs1 = stmt.executeQuery("select count(*) as cnt1 from xxx.... where userId = "+userId);
if(rs1.next())
String count1 = rs1.getString("cnt1");
rs1.close();
ResultSet rs2 = stmt.executeQuery("select count(*) as cnt2...");
if(rs2.next())
String count2 = rs2.getString("cnt2");
rs2.close();
....
rs10.close();
Since this is inefficient, I was hoping to get past Resultset each time by writing some sort of direct query to retrieve each different count like
String cnt1 = stmt.executeQuery("select count(*) as noE from useractiontable where curr_action='edit'" + " and userId = " + userId).getString("noE");
I know something like this cannot be done without using rs.next() each time. Is prepare statement the way to go? Is there another way? Appreciate any pointers in this regard.
You can use group by to retrieve all the user with count(*)
ResultSet rs = stmt.executeQuery("select userid,count(*) from tablename group by userid");
I suspect that you can do all the DB work in one query and then just have one result set to read. It would be much more efficient and much cleaner code. I am thinking something like:
select userid, count(*) as cnt
from tablename t inner join othertablename ot on t.userid = ot.userid
group by userid
If you have other columns in tablename that you want, you would add them to the select and to the group-by. For example:
select userid, username, count(*) as cnt
from tablename t inner join othertablename ot on t.userid = ot.userid
group by userid, username
The above queries will not return userid's with a count of zero. If you want the ones with zeros, use an outer join (and make the counting logic deal with the null case):
select userid, username, isnull(count(ot.userid), 0) as cnt
from tablename t left outer join othertablename ot on t.userid = ot.userid
group by userid, username
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.
I've been trying to figure out why the following code is not generating any data in my ResultSet:
String sql = "SELECT STUDENT FROM SCHOOL WHERE SCHOOL = ? ";
PreparedStatement prepStmt = conn.prepareStatement(sql);
prepStmt.setString(1, "Waterloo");
ResultSet rs = prepStmt.executeQuery();
On the other hand, the following runs properly:
String sql = "SELECT STUDENT FROM SCHOOL WHERE SCHOOL = 'Waterloo' ";
PreparedStatement prepStmt = conn.prepareStatement(sql);
ResultSet rs = prepStmt.executeQuery();
The data type for SCHOOL is CHAR (9 Byte). Instead of setString, I also tried:
String sql = "SELECT STUDENT FROM SCHOOL WHERE SCHOOL = ? ";
PreparedStatement prepStmt = conn.prepareStatement(sql);
String school = "Waterloo";
Reader reader = new CharArrayReader(school.toCharArray());
prepStmt.setCharacterStream(1, reader, 9);
prepStmt.setString(1, "Waterloo");
ResultSet rs = prepStmt.executeQuery();
I'm completely stuck on what to investigate next; the Eclipse debugger says the SQL query doesn't change even after setString or setCharacterStream. I'm not sure if it's because setting parameters isn't working, or if the debugger simply can't pick up changes in the PreparedStatement.
Any help will be greatly appreciated, thanks!
I think the problem is that your datatype is CHAR(9) and "Waterloo" has only 8 chars.
I assume that this would return the expected results (LIKE and %). Or add the missing space.
String sql = "SELECT STUDENT FROM SCHOOL WHERE SCHOOL LIKE ? ";
PreparedStatement prepStmt = conn.prepareStatement(sql);
prepStmt.setString(1, "Waterloo%");
ResultSet rs = prepStmt.executeQuery();
The best way would by to use varchar instead of char if your Strings have a flexible length. Then the PreparedStatement would work as expected.
A workaround would be to use the Oracle specific setFixedCHAR method (but it's better to change the datatype to varchar if possible).
The following is from Oracle's PreparedStatement JavaDoc:
CHAR data in the database is padded to the column width. This leads to a limitation in using the setCHAR() method to bind character data into the WHERE clause of a SELECT statement--the character data in the WHERE clause must also be padded to the column width to produce a match in the SELECT statement. This is especially troublesome if you do not know the column width.
setFixedCHAR() remedies this. This method executes a non-padded comparison.
Notes:
Remember to cast your prepared statement object to OraclePreparedStatement to use the setFixedCHAR() method.
There is no need to use setFixedCHAR() for an INSERT statement. The database always automatically pads the data to the column width as it inserts it.
The following example demonstrates the difference between the setString(), setCHAR() and setFixedCHAR() methods.
// Schema is : create table my_table (col1 char(10));
// insert into my_table values ('JDBC');
PreparedStatement pstmt = conn.prepareStatement
("select count() from my_table where col1 = ?");
ResultSet rs;
pstmt.setString (1, "JDBC"); // Set the Bind Value
rs = pstmt.executeQuery(); // This does not match any row
// ... do something with rs
CHAR ch = new CHAR("JDBC ", null);
((OraclePreparedStatement)pstmt).setCHAR(1, ch); // Pad it to 10 bytes
rs = pstmt.executeQuery(); // This matches one row
// ... do something with rs
((OraclePreparedStatement)pstmt).setFixedCHAR(1, "JDBC");
rs = pstmt.executeQuery(); // This matches one row
// ... do something with rs