So I am working on a small program in Java. Basically I want the user to search a database with one or 1-3 search query and they can use any combination.
A search query example would be: Book Name, ISBN, Author Name
The user can search with any combination (Search by book name only, or book name and author name, or all 3 etc...).
I want to basically write one master SQL statement (MYSQL), that would pull the results from the database. The problem is, I don't know what to do with a blank input.
So say user enters:
Book name = Harry Potter and the Chamber of Secrets
ISBN = << left blank>>
Author Name = JK Rowling
An example SQL query would be:
SELECT *
FROM booklist
WHERE book_name = "Harry Potter and the Chamber of Secrets"
AND ISBN = ""
AND Author = "JK Rowling";
In Java code I am using PreparedStatement, and the String would be:
String temp = " SELECT * " +
" FROM booklist " +
" WHERE Title = " + title +
" AND Author = \"" + author + "\"" +
" AND ISBN = \"" + isbn + "\"";
Because the user did not enter ISBN, the SQL query fails (hence code fails). If I use null instead of "", then the query will still fail.
By fail meaning that it will not find the correct book in database (even though its present), as it is still looking for "" or null in the column.
Is there any way to pass kind of like a 'ghost' or invisible variable to get the SQL query to work like I am intending it to?
Or do I have to go the long way and make a SQL query for each possible combination?
Use OR instead of AND
Change:
String temp = " SELECT * " +
" FROM booklist " +
" WHERE Title = " + title +
" AND Author = \"" + author + "\"" +
" AND ISBN = \"" + isbn + "\"";
To:
String temp = " SELECT * FROM booklist " +
" WHERE Title = ? OR Author = ?" +
" OR ISBN = ?";
And then set the parameters for prepared statement.
pst.setString( 1, title );
pst.setString( 2, author );
pst.setString( 3, isbn );
With the OR if any of the matching records are found then they are fetched.
And, if you still want to use AND for comparison and want to not include empty inputs then you have to dynamically prepare the statement in JAVA.
StringBuilder sql = new StringBuilder( 1024 );
sql.append( " SELECT * FROM booklist " );
String whereCondition = "";
if( title != null && title.trim().length() > 0 ) {
whereCondition += " title = ?";
}
if( isbn != null && isbn.trim().length() > 0 ) {
whereCondition += (whereCondition.length() > 0 ? " AND " : "" );
whereCondition += " isbn = ?";
}
if( author != null && author.trim().length() > 0 ) {
whereCondition += (whereCondition.length() > 0 ? " AND " : "" );
whereCondition += " author = ?";
}
sql.append( " where " ).append( whereCondition );
pst = con.prepareStatement( sql.toString() );
Now set the prepared parameters like this:
And then set the parameters for prepared statement.
int paramIndex = 1;
if( title != null && title.trim().length() > 0 ) {
pst.setString( paramIndex++, title );
}
if( isbn != null && isbn.trim().length() > 0 ) {
pst.setString( paramIndex++, isbn );
}
if( author != null && author.trim().length() > 0 ) {
pst.setString( paramIndex++, author );
}
Now, you can execute the statement and fetch the resultset.
Related
What is the standard approach to update a boolean column (i.e. a categorical binary INTEGER value) in a SQL (SQLite) table?
(here binary is intended to mean an INTEGER that takes two values only: 0 and 1, hence the quotes in the question title)
Given a table:
CREATE TABLE types (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT,
enabled INTEGER DEFAULT 1)
And a list of objects which can have an enabled/disabled state and which are represented as two lists:
List<Long> idsEnabled = new ArrayList<>();
List<Long> idsDisabled = new ArrayList<>();
Would this be the correct way to store the enabled state (i.e. 0 or 1)?
SQLiteDatabase db = dbHelper.getWritableDatabase();
// upd: NOT WORKING because rawQuery does NOT work for modifying data,
// must use execSQL
db.rawQuery("UPDATE types set enabled=1 where id in ("
+ TextUtils.join(",", idsEnabled) + ")", null);
db.rawQuery("UPDATE types set enabled=0 where id in ("
+ TextUtils.join(",", idsDisabled) + ")", null);
Is there a better, more efficient, OOTB way to do this? My assumption is that updating each row one by one would be the worst case.
The question is not necessarily android-specific, although if there is some android-specific API for doing this it would be helpful.
The method rawQuery() is not the way to update the table.
It is used to return rows in the form of a Cursor.
In this case you must use execSQL():
if (idsEnabled.size() > 0 || idsDisabled.size() > 0) {
String ids1 = TextUtils.join(",", idsEnabled);
String ids0 = TextUtils.join(",", idsDisabled);
String sql =
"UPDATE types " +
"SET enabled = CASE " +
(idsEnabled.size() > 0 ? "WHEN id IN (" + ids1 + ") THEN 1 " : "") +
(idsDisabled.size() > 0 ? "WHEN id IN (" + ids0 + ") THEN 0 " : "") +
"ELSE enabled " +
"END";
db.execSQL(sql);
}
The above statement uses a CASE expression to determine the value that will update the current value of the column.
If you want to update just a small part of the table then it would be more efficient to include a WHERE clause in the UPDATE statement, so you can omit the ELSE part of the CASE expression:
if (idsEnabled.size() > 0 || idsDisabled.size() > 0) {
String ids1 = TextUtils.join(",", idsEnabled);
String ids0 = TextUtils.join(",", idsDisabled);
String idsAll = "";
if (idsEnabled.size() == 0) idsAll = ids0;
else if (idsDisabled.size() == 0) idsAll = ids1;
else idsAll = ids1 + "," + ids0;
String sql =
"UPDATE types " +
"SET enabled = CASE " +
(idsEnabled.size() > 0 ? "WHEN id IN (" + ids1 + ") THEN 1 " : "") +
(idsDisabled.size() > 0 ? "WHEN id IN (" + ids0 + ") THEN 0 " : " ") +
"END " +
"WHERE id IN (" + idsAll + ")";
db.execSQL(sql);
}
I've been trying to solve this issue for the past couple of days. I have a SerachUser function where I input all the data like age, gender, city and interests into each string and check them into a select query command.
If data is present, I print them out.
Unfortunately the search isn't working completely. For eg: my table user doesn't have 'F' gender. But if I type 'F' I still get data instead of displaying "ResultSet in empty in Java".
Below is a brief code I have done.
try{
conn = DriverManager.getConnection(DB_URL,"shankarv5815","1807985");
st = conn.createStatement();
rs = st.executeQuery("Select loginID, f_name, l_name from users where gender = '" +
searchUser.getGender() + "' and age between '" + min + "' and '" + max + "' and city = '" +
searchUser.getCity() + "' and interest1 = '" + searchUser.getInterest1() +
"' or interest2 = '" + searchUser.getInterest1() + "' or interest3 = '" +
searchUser.getInterest1() + "' and loginID != '" + curUser + "'");
if (rs.next() == false) {
System.out.println("ResultSet in empty in Java");
}
else {
do {
String id = rs.getString(1);
String fName = rs.getString(2);
String lName = rs.getString(3);
System.out.print(id +" ," + fName + ", " + lName);
System.out.println();
} while (rs.next());
}
}
catch(SQLException e){
e.printStackTrace();
}
finally
{
try
{
conn.close();
st.close();
rs.close();
}
catch(SQLException e)
{
e.printStackTrace();
}
}
A reduced version of your query is :
Select * from users
Where gender = 'F'
And interest1 = 'FISHING'
Or interest2 = 'FISHING'
However, AND has higher priority than OR, so this query is equivalent to :
Select * from users
Where ( gender = 'F' And interest1 = 'FISHING')
Or interest2 = 'FISHING'
What you need to do is add brackets, so :
Select * from users
Where gender = 'F'
And ( interest1 = 'FISHING' Or interest2 = 'FISHING')
By the way, you are also leaving yourself wide open to a SQL injection attack, by including the search terms directly in the SELECT statement ( see What is SQL injection? ).
Much better would be to get in the habit of always using a PreparedStatement.
i like to change the old one into new and my old sql query its in statement and i like to change into prepared statement . how could i change ?
for example :
*
strSql = "SELECT Id as GroupId, Name,Description FROM Groupcodes WHERE deleteFlag=0 and GroupType = '" + StrGroupType + "' ";
if(strSearchBy.length() > 0 && strSearchText.length() > 0)
{
if(strSearchOption.equalsIgnoreCase("Starts With")) {
strSql += " AND " + strSearchBy + " LIKE '" + strSearchText + "%' ";
}
else if(strSearchOption.equalsIgnoreCase("Contains")){
strSql += " AND " + strSearchBy + " LIKE '%" + strSearchText + "%'" ;
}
}
strSql += " ORDER BY Name ASC";
if( nCounter > 0 ) {
strSql += " LIMIT " + (nCounter - 1) + ", " + nMaxCount;
}
*
once you teach for this example then i will do for upcoming codes .
You do it like this:
PreparedStatement mStatement = getDBTransaction().createPreparedStatement("select * from test_table where user_id = ?", 0);
mStatement.setString(1, "4913");
ResultSet rs = mStatement.executeQuery();
The '?' character parametizes the query and later u set its value using setString. Note in the setstring function the first parameter is indicated by index 1 (not 0 ). Also note u dont need ' ' if your where condition is comparing varchars
Today i was doing self-review of my code and code didn't look nice to me (Although it is working as intended). So i want a better way of doing the same.
Following is the case:
In my webservice, client can perform search operation on records by n number of search criteria ( one possible criteria can be get me all the staff whose name contains A and who has designation Teacher. So for such query i have many if statements that is making string query accordingly. but this is looking ugly to me. Is their any way to achieve this using PreparedStatement.
Ugly code looks like below
private String getSearchQuery(Staff staffEntity) {
boolean hasAnySearchParam = false;
String query = null;
StringBuilder querybuilder = new StringBuilder("select * from " + DBConstants.StaffBasicInfo.tableName + " left outer join " + DBConstants.StaffAdvInfo.tableName + " on "
+ DBConstants.StaffBasicInfo.staffId + " = " + DBConstants.StaffAdvInfo.staff_adv_info_staffId + " where");
if (staffEntity.getName() != null && (!staffEntity.getName().isEmpty())) {
querybuilder.append(" " + DBConstants.StaffBasicInfo.staffname + " Like '%" + staffEntity.getName() + "%'");
querybuilder.append(" and");
hasAnySearchParam = true;
}
if (staffEntity.getDesignation() != null && (!staffEntity.getDesignation().isEmpty())) {
querybuilder.append(" " + DBConstants.StaffBasicInfo.designation + " Like '%" + staffEntity.getDesignation() + "%'");
querybuilder.append(" and");
hasAnySearchParam = true;
}
if (staffEntity.getAge() != null) {
querybuilder.append(" " + DBConstants.StaffBasicInfo.age + " = " + staffEntity.getAge());
querybuilder.append(" and");
hasAnySearchParam = true;
}
if (staffEntity.getUsername() != null && (!staffEntity.getUsername().isEmpty())) {
querybuilder.append(" " + DBConstants.StaffAdvInfo.username + " Like '%" + staffEntity.getUsername() + "%'");
querybuilder.append(" and");
hasAnySearchParam = true;
}
if (staffEntity.getRole() != null && (!staffEntity.getRole().isEmpty())) {
querybuilder.append(" " + DBConstants.StaffAdvInfo.role + " Like '%" + staffEntity.getRole() + "%'");
querybuilder.append(" and");
hasAnySearchParam = true;
}
if (false == hasAnySearchParam) {
throw new IllegalArgumentException("Check Json: No parameter to search");
} else {
// need to clean query.
query = querybuilder.substring(0, querybuilder.length() - 3);
}
return query;
}
Note Right now i am more concerned about code clarity, simplicity and ease to use, i can think of performance issues later.
For this particular case I would use static query with params where params are set from java
WHERE
...
and (:userNameParam is null or staffname like concat('%',:staffnameParam,'%'))
and (:userNameParam is null or username like concat('%',:userNameParam ,'%'))
...
Then just pass parameters userNameParam, userNameParam etc. If they are null or empty just pass null.
Your way leaves possibility for SQL injection
I try to multiple select in java. I have two string "name" and "artist" i want to select them both in one query even if one of them is null .
I did something like that :
if ( !nameIsEmpty && !artisIsEmpty )
{
rst = stmt.executeQuery("SELECT * FROM school.product_table where name=" + "'" + name + "' and artist=" + "'" + artist + "'");
}
else if ( nameIsEmpty && !artisIsEmpty )
{
rst = stmt.executeQuery("SELECT * FROM school.product_table where artist=" + "'" + artist + "'");
}
else if ( !nameIsEmpty && artisIsEmpty )
{
rst = stmt.executeQuery("SELECT * FROM school.product_table where name=" + "'" + name + "'");
}
else
{
productIsEmpty = true;
}
I think its not the best way to do this. And I hope there is a easy way to this in one query.
Thanks in advance.
SELECT * FROM school.product_table where name in(name.null) and artist in (name,null)
Along with matching the records with name and artist, it will also return results with name=null and artist, artist=null and name, null and null.
If you want to skip both null results try this -
SELECT * FROM school.product_table where name in(name.null) and artist in (name,null) and !(artist=null && name=null)