SQLite Android Inner join - android

I have one strange issue. I am fetching data from server and inserting it into tables. After insertion I query that data using inner join between two tables.
here is my query :
Select
F._id, F.id, F.logo, F.total_like, F.distance, F.store_name, F.mob_no_1,
F.mob_no_2, F.mob_no_3, F.tel_no_1, F.tel_no_2, F.tel_no_3, F.description,
R.total_record, R.total_page, R.current_page
from
FAVOURITE_STORES as F
INNER JOIN FAVOURITE_RECORDS as R on F.area_id = R.area_id
where
F.area_id = 2 and
R.area_id = 2
I get the cursor count as 1 on some devices and on some devices I get cursor count as zero. Even if the data is there in table.
here is my select query function
public Cursor rawQuery(String sQuery,String[] selectionArgs) {
if(mDatabase == null) {
mDatabase = getWritableDatabase();
}
debug("Query "+sQuery);
return mDatabase.rawQuery(sQuery,selectionArgs);
}
Cursor class
public class ExampleCursorLoader extends CursorLoader {
private Activity mActivity;
private String msQuery;
private DBUtil mDbUtil;
private String[] mSelectionArgs;
public ExampleCursorLoader(Activity context, String query,String[] selectionArgs) {
super(context);
this.mActivity = context;
this.msQuery = query;
this.mSelectionArgs = selectionArgs;
this.mDbUtil = DBUtil.getInstance(mActivity.getApplicationContext());
}
public ExampleCursorLoader(Activity context, String query) {
this(context,query,null);
debug(query);
}
public Cursor loadInBackground() {
debug("Loading in Background");
Cursor cursor=null;
cursor = mDbUtil.rawQuery(msQuery,mSelectionArgs);
return cursor;
}
private void debug(String s) {
Log.v("Adapter " , "Adapter " + s);
}
protected void onStartLoading() {
forceLoad();
debug("Started Loading");
}
protected void onStopLoading() {
super.onStopLoading();
}
}
and this is how I am calling it.
return new ExampleCursorLoader(mActivity,sQuery);
Device that gives count as 1 is Samsung s3. And zero is Samsung Grand.
Have any thought or suggestion on this?

Device that gives count as 1 is Samsung s3. And zero is Samsung Grand.
Have any thought or suggestion on this?
This kind of problem is hard to explain because most likely it doesn't throw any error but only shows different and unwanted result.
All what you are able to do is to "improve query":
At first use placeholders and not raw statements:
String query = "... where F.area_id = ? and R.area_id = ?";
String[] whereArgs = {"2", "2"};
Also you don't need to use AS clause, it's enough to say:
... from FAVOURITE_STORES F INNER JOIN FAVOURITE_RECORDS R on ...
From code that you provided everythings seems correct (query and an usage of rawQuery()).

It seems to me that the last section of your WHERE-clause is unnecessary:
and R.area_id=2
You've already specified the rows for the FAVOURITE_STORES table to match area_id=2. As the INNER JOIN is performed on that column, it will only return the rows from FAVOURITE_RECORDS where the area_id column matches FAVOURITE_STORES' area_id column.
I'm not sure how the SQLite engine handles this, but it's worth a try.

Related

Android SQLite Database check if query exists?

Hi i'm still new in android and SQLite. I got activity which that can add query to my attached db file.
The problem is, i can't add data using my methods. Is there a simple methods to check if query is exists ?
Here is my DB Access
public class DBAccess {
private SQLiteOpenHelper openHelper;
private SQLiteDatabase db;
private static DBAccess instance;
Cursor c = null;
private DBAccess(Context context)
{
this.openHelper = new DatabaseOpenHelper(context);
}
public static DBAccess getInstance(Context context)
{
if(instance==null)
{
instance=new DBAccess(context);
}
return instance;
}
public void open()
{
this.db = openHelper.getWritableDatabase();
}
public void close()
{
if(db!=null)
{
this.db.close();
}
}
public void tambah(String a,String b)
{
String query= ("insert into TabelAbjad (kata,arti) values('"+a+"','"+b+"')");
db.execSQL(query);
}
public boolean checkdata(String c, String d)
{
String s;
String query= ("select kata from TabelAbjad where kata = '"+c+"' AND kata = '"+d+"'");
db.execSQL(query);
return true;
}
Here is when i try to call the methods
private void adddata()
{
DBAccess dbAccess = DBAccess.getInstance(getApplicationContext());
dbAccess.open();
String k = editkata.getText().toString().trim();
String a = editarti.getText().toString().trim();
if (dbAccess.checkdata(k,a))
{
Toast.makeText(this, "Data already exists",Toast.LENGTH_LONG).show();
dbAccess.close();
finish();
}
else
{
dbAccess.tambah(k,a);
dbAccess.close();
Toast.makeText(this, "Data Saved",Toast.LENGTH_LONG).show();
}
}
P.S : I'm call the method in button
You are missing a semicolon (;) in your query string.
Try this String query= ("select kata from TabelAbjad where kata = '"+c+"' AND kata = '"+d+"';"); for all your query strings
Your issue is that no row in the table will exist where the column kata has the value c as well as (AND) the value d it is impossible as a column can only have a single value, thus no rows would ever be extracted.
Perhaps you want to find rows that have either c OR d
in which case you could use :-
String query= ("select kata from TabelAbjad where kata = '"+c+"' OR kata = '"+d+"'");
i.e. AND has been changed to OR
Furthermore, execSQL cannot return a result, as per :-
Execute a single SQL statement that is NOT a SELECT or any other SQL
statement that returns data. execSQL
You thus need to either use the rawQuery method or the convenience query method, the latter recommended unless it's limitations mean that it cannot be used. As such you should try using something along the lines of (OR instead of AND assumed) :-
public boolean checkdata(String c, String d)
boolean rv = false;
String whereargs = "kata=? OR kata=?"; // the WHERE clause less the WHERE keyword ? for the arguments (used on a 1 for 1 basis)
String[] whereargs = new String[]{c,d}; // The arguments that will be substituted into the SQL
String[] columns = new String[]{"kata"};
Cursor csr = db.query("TabelAbjad",columns,whereclause,whereargs,null,null,null);
if (csr.getCount() > 0) {
rv = true;
}
csr.close();
return rv;
}
Although this may appear to be more complex to code it has advantages as it build the underlying SQL, it protects against SQL injection and it correctly encloses/escapes (wraps the values in single quotes etc) the values)arguments
You retrieve data (rows) into a Cursor (in this case you simply want to know if any rows matched the given criteria so getCount() is used (it returns the number of rows extracted which could be 0 if none)).
Is there a simple methods to check if query is exists ?
I believe that what you really mean is there anyway to check if the query returned any results.
As said above a query returns data via a Cursor (like a table but according to the query e.g. the Cursor above will consist of a number of rows (0 or more) with a single column named kata (i.e. the query is SELECT **kata** FROM .....) ) and thus you need to use a suitable method.
You can check/access numerous aspects/properties. Typically you'd move around the Cursor (e.g. while (your_cursor.moveToNext) {.... do things ....} can be used traverse all rows in the cursor). Cursor.
Once positioned appropriately at a row then you can use the get????(column_offset) to retrieve the data that came from the database (where column_offset is an integer with 0 representing the first column, however it is generally much wiser to retrieve the actual offset using the getColumnIndex(column_name_as_a_string method.))
So assuming that you wanted the data (String) in the first row that was extracted above into the Cursor csr then you could use :-
if (csr.moveToFirst()) {
String mykata = csr.getString(csr.getColumnIndex("kata"));
}
csr.close(); // You should always close a cursor when done with it.

Best practice to implement method getXXX from db using cursor for Android

For example code below. Do we need to close cursor? Do we better use try/catch/finally instead of using if()?
public int getCount() {
final Cursor countCursor = contentResolver.query(
AnalyticContract.CONTENT_URI,
new String[] {"count(*) AS count"},
null,
null,
null);
if (countCursor == null) {
return 0;
}
countCursor.moveToFirst();
final int count = countCursor.getInt(0);
return count;
}
The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource.
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
The answer I believe is primarily opinion-based. It depends I guess on coder's preference and the circumstances.
I have always preferred the if (cursor != null) or vice versa approach. Unless something truly spectacular has happened; which will be handled by throws Exception, I'd use if-else checks wherever I want the reader/reviewer to see which parts are really and truly exceptions and which are occurrences of different possible/valid scenarios.
This brings us to the current problem of Curosr and applying null checks.
AFAIK (since mostly a Cursor is related with a SQLiteDatabase) a ContentResolver.query() should never return a null Cursor if the query itself is valid unless in case of an invalid query which is a real exception and you should instead get an Exception.
So in my opinion the best approach would be using your example either
public int getCount() throws Exception {
Cursor countCursor;
try {
countCursor = contentResolver.query(
AnalyticContract.CONTENT_URI,
new String[] {"count(*) AS count"},
null,
null,
null);
countCursor.moveToFirst();
return countCursor.getInt(0);
}
finally {
cursor.close();
}
}
Or a variation where Exception is caught and handled within the method itself.
Now to answer your second question whether or not you should close() a Cursor: you should always close a Cursor. Whenever you don't have need for it. If you delve deeper into any of the Cursor.close() method-implementations. Since Curosr is an interface which in case of SQLite is implemented by SQLiteCursor you will notice that this method releases any and all allocations held by it.
I prefer to make a database helper class and through that database access becomes much much easier. Sample of a database helper Class -
public class DatabaseHelperClass extends SQLiteOpenHelper {
public DatabaseHelperClass(Context context)
{
super(context,"hakeem.db",null,1);
}
//Tables
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("Your SQL Query to create a table");
}
//Delete Tables
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table table_name if exists");
onCreate(db);
}
//Insertion of data into tables
long insertData(Various Parameters you like to pass for insertion)
{
SQLiteDatabase db=getWritableDatabase();
ContentValues values=new ContentValues();
values.put("col_name1",value);
values.put("col_name2",value);
values.put("col_name3",value);
values.put("col_name4",value);
return db.insert("signup_details",null,values);
}
//Delete record
public int deleteData(int id)
{
SQLiteDatabase sb=getWritableDatabase();
return sb.delete("hospital_details","id="+id,null);
}
//Update data in table
int updateData(Parameters you want to pass for update. Make sure you include a primary key)
{
ContentValues values=new ContentValues();
values.put("col_name1",value);
values.put("col_name2",value);
values.put("col_name3",value);
values.put("col_name4",value);
return getWritableDatabase().update("signup_details",values,"id="+id,null);
}
//Read data from tables
Cursor getSigninDetails() { return getWritableDatabase().rawQuery("select * from table_name",null); }
}
and to access results from the database-
private void getDataFromDatabase() {
Cursor cursor = db.getUserData();
if (cursor.moveToFirst()) {
do {
var_name1= cursor.getString(0);
var_name2= cursor.getString(1);
var_name3= cursor.getString(2);
var_name4= cursor.getString(3);
} while (cursor.moveToNext());
}
cursor.close();
}

retrieve data from database what next?

ok I just followed an instruction that I should do this to retrieve sql data from database but it just cuts to there so far I have this inside my databasehelper class.
public void getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
}
while (cursor.moveToNext());
}
db.close();
}
So somehow this does is it get all the values of my tables 4th column which contains an int... how do I retrieve the value in my MainActivity and save it in an array of integers?
just add everything in a ArrayList and return the arraylist
simply call the method in your main activty
public ArrayList<Integer> getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
ArrayList data= new ArrayList<>();
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
data.add(resource);
}
while (cursor.moveToNext());
}
db.close();
}
return data;
}
Well, as you have it, the variable resource is scoped only to the while loop. Even if it wasn't it would constantly get overwritten on each loop iteration.
Instead, you should declare a collection higher up and Add each value to it during your while loop. You could also redefine your function to return the collection if integers.
public List<int> getIconResource(String tblName)
{
SQLiteDatabase db = this.getReadableDatabase();
List<int> myVals = new List<int>();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource, null); //null for conditions
if (cursor.moveToFirst())
{
do
{
myVals.Add(cursor.getInt(3));
}
while (cursor.moveToNext());
}
db.close();
return myVals;
}
Also, as a note... string concatenation of a SQL query is a recipe for disaster. Look up SQL Injection and best practices to avoid it before continuing further. It is worth the time to get into good habits early on.
EDIT / ADDENDUM
Unless you also limit your result set returned from your table query, you will be getting every record. The function you have here really has no practical use and would likely cause more problems than any benefits it may have. I would suggest, as an example of a more usable function that returns a specific IconResource based on the IconId:
public int getIconResource(int iconId)
{
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "select IconResource from IconTable where IconId = ?";
PreparedStatement pstmnt = db.prepareStatement(getresource);
pstrmnt.setString(1, iconId);
ResultSet rset = db.executeQuery();
int iconResource;
if (rset.next())
iconResource = rset.getInt("IconResource");
db.close();
return iconResource;
}
Of course, the above is making assumptions of your table structure.
Using the above, in your code elsewhere, you would simply call this function with the IconId and use the output however needed:
int iconResource = getIconResource(5); // returns the IconResource for IconId = 5
The above prevents any possible SQL Injection attacks by using a parameterized query and avoiding the use of dynamic concatenated strings sent to your SQL server.
You may try out the following code:
public List<Integer> getIconResource(String tblName)
{
List<Integer> list = new ArrayList<Integer>();
list.clear();
SQLiteDatabase db = this.getReadableDatabase();
String getresource = "Select * from " + tblName;
Cursor cursor = db.rawQuery(getresource,null); //null for conditions
if(cursor.moveToFirst())
{
do
{
int resource = cursor.getInt(3);
list.add(resource);
}
while (cursor.moveToNext());
}
db.close();
return list;
}
Then call this method in MainActivity and store the List in another Integer type list.
databasehelper dbhelper;
List<Integer> newList = dbhelper.getIconResource("Your tablename");
fot(int i = 0 ; i< newList.size() ; i++){
int yourValue = newList(i);
}

Searching and retrieving specific data from a previously created SQLite Database in Android?

I have an SQLite Database which I made in SQLite Browser and exported to my Android Application through SQLite Asset Helper Library. This database is for reading-only tasks and can't be upgraded or modified.
Now I want to search in an specific column any data the user input in a TextView, and in case the data matches with some value of the column retrieve all the values corresponding from the other columns.
For example:
TABLE FRUITS
_id NAME URL
1 apple R.drawable.apple
2 orange R.drawable.orange
3 kiwi R.drawable.kiwi
Since my application only allows the user to input the NAME field, the query needs to search in the NAME column only, and retrieve the URL value only if exists in the database, otherwise will return nothing.
Here's my code for importing the database:
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;
public class MyDatabase extends SQLiteAssetHelper {
private static final String DATABASE_NAME = "fruitManager";
private static final int DATABASE_VERSION = 1;
public MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public Cursor answerRequest(String request) {
SQLiteDatabase db = getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String sqlTables = "FRUITS";
qb.setTables(sqlTables);
String[] sqlSelect = {"0 _id", "NAME", "0 URL"};
Cursor c = qb.query(db, sqlSelect, null, null,
null, null, null);
return c;
}
}
And from here I made the request:
public class Request {
private Cursor cursor;
private MyDatabase db;
String request;
String url;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite_asset_helper);
public void onClick(View v) {
request = mEdit.getText().toString().toLowerCase();
db = new MyDatabase(this);
cursor = db.answerRequest(request);
url = cursor.getString(c.getColumnIndex("NAME"));
System.out.println("Your request was: " + request +
" and has this URL: " + url);
}
}
protected void onDestroy() {
super.onDestroy();
cursor.close();
db.close();
}
Still, loads of code are needed for do the query but I don't know exactly how to implement them in this case (due to imported database). Thanks in advance.
UPDATE: I'm kinda desperate so if you don't have the answer but know an alternative for do the task (like using another database handler instead of SQLite) I will accept your suggestions as the correct answer.
Only restriction is: Has to be an offline solution (no internet connection needed for access the database).
Your current query looks like this:
`select 0 _id, NAME, 0 URL from FRUITS`
Notice you aren't using the request string anywhere. You are also selecting integer literal 0 and aliasing this to other column names--there doesn't seem to be any reason for this. As is, your results would be
_id NAME URL
0 apple 0
0 orange 0
0 kiwi 0
What you want is the following:
`select * from FRUITS where NAME = request`
Here's how I would write it:
public Cursor answerRequest(String request) {
SQLiteDatabase db = getReadableDatabase();
String table = "FRUITS";
String where = "NAME = ?";
String[] whereArgs = {request};
// select * from fruits where name = request
return db.query(table, null, where, whereArgs, null, null, null);
}
And elsewhere when you want to retrieve the values:
db = new MyDatabase(this);
Cursor cursor = db.answerRequest(request);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
// cursor is not empty, read values here
String url = cursor.getString(cursor.getColumnIndex("URL"));
}
} finally {
cursor.close();
}
}

Check if some string is in SQLite database

I have some trouble with a SQLite database with 1 table and 2 columns, column_id and word. I extended SQLiteAssetHelper as MyDatabase and made a constructor:
public MyDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
I need to check whether some string is in the database (in column word). I tried to modify the code from answer provided by Benjamin and dipali, but I used SQLiteAssetHelper and I can't get it to work. The method that I have in mind receives the string to search for as a parameter and returns a boolean if string is in the database.
public boolean someMethod(String s)
In addition, I tried to put the check on a background thread with AsyncTask because I have 60 strings to check.
TABLE_NAME and COLUMN_WORD should be self-explanatory.
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[] {COLUMN_WORD};
String where = COLUMN_WORD + " = ?";
String[] whereArgs = new String[] {s};
// select column_word from table where column_word = 's' limit 1;
Cursor cursor = db.query(TABLE_NAME, columns, where, whereArgs, null, null, null, "1");
if (cursor.moveToFirst()) {
return true; // a row was found
}
return false; // no row was found
}
You can do this in the background, but I don't think for a query like this it's even necessary.
EDIT
There are some improvements that should be made to the above for the sake of correctness. For one thing, the Cursor should be closed since it is no longer being used. A try-finally block will ensure this:
Cursor cursor = db.query(...);
try {
return cursor.moveToFirst();
} finally {
cursor.close();
}
However, this method doesn't need to obtain a whole `Cursor. You can write it as follows and it should be more performant:
public boolean someMethod(String s) {
SQLiteDatabase db = getReadableDatabase();
String sql = "select count(*) from " + TABLE_NAME + " where "
+ COLUMN_WORD + " = " + DatabaseUtils.sqlEscapeString(s);
SQLiteStatement statement = db.compileStatement(sql);
try {
return statement.simpleQueryForLong() > 0;
} finally {
statement.close();
}
}
You could add a catch block and return false if you think it's possible (and valid) to encounter certain exceptions like SQLiteDoneException. Also note the use of DatabaseUtils.sqlEscapeString() because s is now concatenated directly into the query string and thus we should be wary of SQL injection. (If you can guarantee that s is not malicious by the time it gets passed in as the method argument, then you could theoretically skip this, but I wouldn't.)
because of possible data leaks best solution via cursor:
Cursor cursor = null;
try {
cursor = .... some query (raw or not your choice)
return cursor.moveToNext();
} finally {
if (cursor != null) {
cursor.close();
}
}
1) From API KITKAT u can use resources try()
try (cursor = ...some query)
2) if u query against VARCHAR TYPE use '...' eg. COLUMN_NAME='string_to_search'
3) dont use moveToFirst() is used when you need to start iterating from beggining
4) avoid getCount() is expensive - it iterates over many records to count them. It doesn't return a stored variable. There may be some caching on a second call, but the first call doesn't know the answer until it is counted.

Categories

Resources