android noob... I have two tables, with a one to many relationship between country_tbl and city_tbl, and I would like to concatenate values from city_tbl.landmark_col with GROUP_CONCAT() and INSERT all the landmark_col values as a single String into country_tbl.all_landmarks column. The SQL seems to require a nested SELECT to concatenate the landmark_col values before passing them to the country_tbl... something like:
UPDATE country_tbl
SET country_tbl.all_landmarks = (SELECT landmarks_col FROM
(SELECT country_id, group_concat(landmarks_col)
FROM city_tbl INNER JOIN country_tbl
ON country_tbl.country_id = city_tbl.country_id
GROUP BY country_tbl.country_id)
AS country_landmarks
WHERE country_tbl.country_id = country_landmarks.country_id)
WHERE
EXISTS (
SELECT *
FROM country_landmarks
WHERE country_tbl.country_id = country_landmarks.country_id
);
Not sure if nested select statements are even supported or if just too resource intensive... there must be a better way, as it seems like using rawQuery is not the best solution. Not sure if I should be creating temporary tables, using ContentProviders, or passing a cursor...?
I answered this by splitting up the long SQL query into two parts. First I created a subquery with a SQLiteQueryBuilder and ran using rawQuery to get a two column cursor with the location_id and the group_concat values for the landmark_names. I was then able to cycle through the cursor to update the country table with each of the appropriate concatenated values of all the landmark names for that country.
The query below is a tad more complicated than the question above (which I simplified before posting), only because I had to join a landmarks list table with another landmark_type table by the landmark_type_id, and my real goals was to concatenate the shorter list of landmark_type by country, not the long list of all the landmark_names by country. Anyway, it works.
public void UpdateCountryLandmarks() throws SQLException {
Cursor c = null;
String subquery = SQLiteQueryBuilder.buildQueryString(
// include distinct
true,
// FROM tables
LANDMARK_TYPE_TABLE + "," + LANDMARKS_TABLE,
// two columns (one of which is a group_concat()
new String[] { LANDMARKS_TABLE + "." + LOCATION_ID + ", group_concat(" + LANDMARK_TYPE_TABLE + "." + LANDMARK_TYPE + ",\", \") AS " + LANDMARK_NAMES },
// where
LANDMARK_TYPE_TABLE + "." + LANDMARK_ID + "=" + LANDMARKS_TABLE + "." + LANDMARK_TYPE_ID,
// group by
LANDMARKS_TABLE + "." + LOCATION_ID, null, null, null);
c = mDb.rawQuery(subquery, null);
if (c.moveToFirst()) {
do {
String locationId = c.getString(c.getColumnIndex(LOCATION_ID));
String landmarkNames = c.getString(c.getColumnIndex(LANDMARK_NAMES));
ContentValues cv = new ContentValues();
cv.put(LANDMARK_NAMES, landmarkNames);
mDb.update(COUNTRY_TABLE, cv, LOCATION_ID + "=" + locationId, null);
} while (c.moveToNext());
}
c.close();
}
Related
I'm a newbie with Android Studio so please be patient... This forum often leads me with suggestions and examples (as a reader), but today I decided to ask for help:
Since hours, I try to build an SQLite statement in Android Studio: There is a column COLUMN_LAST_ATTEMPT with date and time as String, e.g. 2020-01-09 17:23, see screenshot, and I want to get the newest date (without time) from the table, e.g. 2020-09-01. I tried various options but I can't get it to run.
What I need is an Android SQLite Statement for
SELECT MAX(SUBSTR(last_attempt,11,20)) FROM quiz_questions
(which runs on DBBrowser), where 'last attempt' is a column of table 'quiz_questions', screenshot of that column in table 'quiz_questions'
I tried the following rawQueries, none of them works:
In QuizDBHelper-Class
//...
final QuizDbHelper dbHelper = QuizDbHelper.getInstance(this);
//...
public String newestQuiz(){
db = getReadableDatabase();
String result = null;
Cursor cursor = db.rawQuery("SELECT MAX(" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + ") FROM "
+ QuizContract.QuestionsTable.TABLE_NAME, null);
//Cursor cursor = db.rawQuery("SELECT MAX(SUBSTR(" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT +
// ",11,20)) FROM " + QuizContract.QuestionsTable.TABLE_NAME, null);
//Cursor cursor = db.rawQuery("SELECT " + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + " FROM " +
// QuizContract.QuestionsTable.TABLE_NAME, null);
if(cursor.moveToFirst()){
do {
result = cursor.getString(c.getColumnIndex(QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT));
} while (cursor.moveToNext());
}
cursor.close();
return result;
}
In Statistics-Class
String LastUse = dbHelper.newestQuiz();
LastUsage.setText("Letzte Challenge: " + LastUse);
//LastUsage is a TextView in activity_Statistics.xml
//attached with LastUsage = findViewById(R.id.text_lastUsage);
Either the SQLite statements are totally wrong or I make (basic?) mistakes in statistics class. I need ...newbie help!
I need something like Select column from table where substring of date-Entry == newest
Your issue appear to be column names. That is a Cursor only contains the columns extracted, not all the columns from the table. Although you are basing your query on the column as per QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT that will not be the column name in the cursor.
Rather it will will MAX(SUBSTR(" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT +
// ",11,20))
The simplest way of managing this is to give the column in the Cursor a specific name using AS. As such perhaps use :-
Cursor cursor = db.rawQuery("SELECT MAX(" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + ") AS " + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + " FROM "
+ QuizContract.QuestionsTable.TABLE_NAME, null);
However, you may prefere to use a column name (AS ????) specififc to the situation e.g.
........ AS max_" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT + ........
You would then have to use :-
result = cursor.getString(c.getColumnIndex("max_" + QuizContract.QuestionsTable.COLUMN_LAST_ATTEMPT));
Alternately, as it's just a single value/column that is returned in the cursor you could use the column offset of 0, in which case the column name is irrelevant as long as it is valid. However, using offsets is not typically recommended due to the lack of validation of the column being accessed.
re the comment :-
I just need the date part
As the date is a recognised DateTime format (and also that such formats are directly sortable/orderable), use max(date(column_name)) or even max(column_name).
Recently I got to know that raw query in android can not prevent SQL injection and thus I decided to convert all queries in Prepared statement which is SQL injection prevention. But I don't know how to convert complex queries in Prepared Statement.
I want to convert below queries:
1.
select
*
FROM
TableName
where
(tab1col1 in(SELECT tab2Col2 FROM MasterTable where tab2col1='Y')
or tab1col2 = CV.TRUE)
order by
tab1col3, tab1col4, tab1col5,tab1col6
2.
Select
* ,count(*) as TOTAL_COUNT ,
SUM(CASE WHEN tabCol1 LIKE '%todayDate%' THEN 1 ELSE 0 END) as TOTAL_COL1_COUNT
from
TableName
group by tabCol2;
You can use rawQuery to prevent injection by passing any arguments via the selectionargs (2nd parameter).
SQL injection, wouldn't apply to either of the queries, as they are hard coded and have no user generated/supplied inputs.
e.g. your first query could be (assuming that, 'Y' and CV.TRUE are passed as parameters (i.e. user generated/supplied) for the sake of demonstration) :-
public Cursor query1raw(String indicator1,String indicator2) {
String sql = "SELECT * " +
" FROM TableName " +
" WHERE (tab1col1" +
" IN(" +
" SELECT tab2col2 " +
" FROM MasterTable " +
" WHERE tab2col1=?)" +
" OR tab1col2=?)" +
" ORDER BY tab1col3, tab1col4,tab1col5,tab1col6";
String[] args = new String[]{indicator1,indicator2};
return mDB.rawQuery(sql,args);
}
However, the convenience methods are generally recommended rather than rawQuery or execSQL when they can be used, again using bound strings via arguments, the above, using the query convenience method could be :-
public Cursor query1(String indicator1, String indicator2) {
String whereclause = "(tab1col1 IN(SELECT tab2col2 FROM MasterTable WHERE tab2col1=?) OR tab1col2=?)";
String[] whereargs = new String[] {indicator1,indicator2};
String order_columns = "tab1col3,tab1col4,tab1col5,tab1col6";
return mDB.query("TableName",null,whereclause,whereargs,null,null,order_columns);
}
You wouldn't use prepared statements themselves as they are restricted to returning single values, not a row or rows with multiple columns.
Warning not advised
However, you could, if you really wanted, use :-
public Cursor query1ps(String indicator1,String indicator2) {
String[] whereargs = new String[] {indicator1,indicator2};
SQLiteStatement stmnt = mDB.compileStatement("SELECT * " +
" FROM TableName " +
" WHERE (tab1col1" +
" IN(" +
" SELECT tab2col2 " +
" FROM MasterTable " +
" WHERE tab2col1=?)" +
" OR tab1col2=?)" +
" ORDER BY tab1col3, tab1col4,tab1col5,tab1col6");
stmnt.bindAllArgsAsStrings(whereargs);
Log.d("PREPAREDSQL",stmnt.toString());
String sql = stmnt.toString().replace("SQLiteProgram:","");
return mDB.rawQuery(sql,null);
}
As you can see all the prepared statement is doing as such, is substituting the arguments, so has little benefit over the other methods. This would also be dependant upon SQLIteProgram: remaining constant.
The only way to prevent SQL injections is to use parameters. (In some PHP APIs, the only way to get parameters is to use prepared statements, but that is not one of the warts in the Android database API.)
Just write ? for any string, and pass the values separately:
String name = ...;
String password = ...;
cursor = db.rawQuery("SELECT SomeCol FROM Users WHERE Name = ? AND Password = ?",
new String[]{ name, password });
Please not that SQL injection could happen only if you have string values that are controlled by the (potentially-hostile) user. Your queries above do not look as if this were the case.
I have tableA, tableB, and tableC
table A and tableB are joined by tableA.Id(PK) = tableB.tableAId(FK)
table B and tableC are joined by tableB.Id(PK) = tableC.tableBId(FK)
I want to be able to do this:
SELECT c.ALL from tableC c
INNER JOIN tableB b on c.tableBId = b.Id
INNER JOIN tableA a on b.tableAId = a.Id
WHERE a.Id = 108
I have found a lot of posts on the web which uses db.rawquery() to implement this query. However I have also heard that rawquery() is less secure than query(). So for the sake of seeking best practice as a beginner, my question is:
Is there a way to implement this query using db.query() instead of db.rawquery()?
thanks in advance.
Is there a way to implement this query using db.query() instead of
db.rawquery()?
So it's worth to say that rawQuery() makes a trick. But also exists another approach.
query() method is designed for performing queries over one table. But the best way how to JOIN tables in SQLite is to use SQLiteQueryBuilder and with setTables() method you are able to join.
Hence i recommend you to use mentioned SQLiteQueryBuilder. But it's little more complicated against rawQuery() method where you need to assign only raw statement.
If don't know how to start, check this example:
How to use a join with SQLite
Note:
Is the fact that rawQuery() is less secure than query() because query() method uses precompiled statements which are safer than "raw" statements. But always you can(should) use placeholders which significantly increase safety of statement as main protection against SQL injections and statement becomes much more human-readable as well.
This is kind of late, but I thought others who're looking for that might benefit from that:
db.query() method natively supports LEFT OUTER JOIN AND INNER JOIN via its table argument so you don't actually need to use SQLiteQueryBuilder to accomplish that. Also it's easier and and pretty much straight forward.
This method is widely used in Google I/O 2015 Schedule app's source code.
A Quick example (String constants left out for brevity):
Cursor cursor = db.query(NoteContract.Note.TABLE_NAME
+ " LEFT OUTER JOIN authors ON notes._id=authors.note_id", projection, selection,
selectionArgs, null, null, "notes._id");
The key is in the first argument to db.query().
Currently, only LEFT OUTER JOIN and INNER JOIN are supported, which is quite sufficient for most apps.
I hope this answer helps others who're looking for this.
Yes you can use query() instead of rawQuery(), given a single assumption - there are no two same column names in the tables you are joining.
If that criteria is fullfilled, then you can use this answer
https://stackoverflow.com/a/34688420/3529903
As per SharpEdge's comment and after trying a more complex example based upon Nimrod Dayan's answer, here's a more complex example.
4 joins are used, a generated column is also used. It uses an expression (subtracts timestamps) and then uses that in the WHERE clause.
Basically, the method is to append the join clauses to the table name string (SQLite then moves this for you to after the columns).
DBConstants.SQL????? is resolved to the respective SQL e.g. DBConstants.SQLISNOTNULL resolves to IS NOT NULL
DBConstans.CALCULATED????? are names for calculated columns.
DB????TableConstants.????_COL resolves to column names (.._FULL resolves to table.column e.g. to avoid ambiguous _ID columns).
The method (getToolRules) is as follows :-
public Cursor getToolRules(boolean rulesexist,
int minimumruleperiodindays,
int minimumbuycount) {
String columns[] = new String[] {
"0 " + DBConstants.SQLAS + DBConstants.STD_ID,
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL,
DBProductusageTableConstants.PRODUCTUSAGE_AISLEREF_COL,
DBProductusageTableConstants.PRODUCTUSAGE_COST_COL,
DBProductusageTableConstants.PRODUCTUSAGE_BUYCOUNT_COL,
DBProductusageTableConstants.PRODUCTUSAGE_FIRSTBUYDATE_COL,
DBProductusageTableConstants.PRODUCTUSAGE_LATESTBUYDATE_COL,
DBProductusageTableConstants.PRODUCTUSAGE_ORDER_COL,
DBProductusageTableConstants.PRODUCTUSAGE_RULESUGGESTFLAG_COL,
DBProductusageTableConstants.PRODUCTUSAGE_CHECKLISTFLAG_COL,
DBProductusageTableConstants.PRODUCTUSAGE_CHECKLISTCOUNT_COL,
"(" +
DBProductusageTableConstants.PRODUCTUSAGE_LATESTBUYDATE_COL +
"- " +
DBProductusageTableConstants.PRODUCTUSAGE_FIRSTBUYDATE_COL +
" / (86400000)" +
") " + DBConstants.SQLAS + DBConstants.CALCULATED_RULEPERIODINDAYS,
DBProductsTableConstants.PRODUCTS_NAME_COL,
DBAislesTableConstants.AISLES_NAME_COL,
DBAislesTableConstants.AISLES_ORDER_COL,
DBAislesTableConstants.AISLES_SHOPREF_COL,
DBShopsTableConstants.SHOPS_NAME_COL,
DBShopsTableConstants.SHOPS_CITY_COL,
DBShopsTableConstants.SHOPS_ORDER_COL,
DBRulesTableConstants.RULES_ID_COL_FULL +
DBConstants.SQLAS + DBRulesTableConstants.RULES_ALTID_COL,
DBRulesTableConstants.RULES_AISLEREF_COL,
DBRulesTableConstants.RULES_PRODUCTREF_COL,
DBRulesTableConstants.RULES_NAME_COL,
DBRulesTableConstants.RULES_USES_COL,
DBRulesTableConstants.RULES_PROMPT_COL,
DBRulesTableConstants.RULES_ACTON_COL,
DBRulesTableConstants.RULES_PERIOD_COL,
DBRulesTableConstants.RULES_MULTIPLIER_COL
};
String joinclauses = DBConstants.SQLLEFTJOIN +
DBProductsTableConstants.PRODUCTS_TABLE +
DBConstants.SQLON +
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL + " = " +
DBProductsTableConstants.PRODUCTS_ID_COL_FULL + " " +
DBConstants.SQLLEFTJOIN +
DBAislesTableConstants.AISLES_TABLE +
DBConstants.SQLON +
DBProductusageTableConstants.PRODUCTUSAGE_AISLEREF_COL + " = " +
DBAislesTableConstants.AISLES_ID_COL_FULL +
DBConstants.SQLLEFTJOIN +
DBShopsTableConstants.SHOPS_TABLE +
DBConstants.SQLON +
DBAislesTableConstants.AISLES_SHOPREF_COL + " = " +
DBShopsTableConstants.SHOPS_ID_COL_FULL +
DBConstants.SQLLEFTJOIN +
DBRulesTableConstants.RULES_TABLE +
DBConstants.SQLON +
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL + " = " +
DBRulesTableConstants.RULES_PRODUCTREF_COL +
DBConstants.SQLAND +
DBProductusageTableConstants.PRODUCTUSAGE_AISLEREF_COL + " = " +
DBRulesTableConstants.RULES_AISLEREF_COL
;
String ruleexistoption = DBRulesTableConstants.RULES_ID_COL_FULL;
if (rulesexist) {
ruleexistoption = ruleexistoption + DBConstants.SQLISNOTNULL;
} else {
ruleexistoption = ruleexistoption + DBConstants.SQLISNULL;
}
String whereclause = DBProductusageTableConstants.PRODUCTUSAGE_BUYCOUNT_COL +
" = ?" +
DBConstants.SQLAND + ruleexistoption +
DBConstants.SQLAND +
"(" + DBConstants.CALCULATED_RULEPERIODINDAYS + " / ?) > 0" +
DBConstants.SQLAND +
DBProductusageTableConstants.PRODUCTUSAGE_BUYCOUNT_COL + " > ?";
if (minimumbuycount > 0) {
--minimumbuycount;
}
String[] whereargs = new String[] {
"0",
Integer.toString(minimumruleperiodindays),
Integer.toString(minimumbuycount)
};
return db.query(DBProductusageTableConstants.PRODUCTUSAGE_TABLE + joinclauses,
columns,whereclause,whereargs,null,null,null);
}
The base SQL, which was created in SQLite Manager, used as a guide to building the method (looks far nicer, IMHO, than the SQL extracted from the cursor in debug) is :-
Note! 0 AS _ID is used to enable the cursor to be used by a CursorAdapter (i.e. CursorAdapters require a column named _ID)
SELECT
0 AS _id,
productusage.productusageproductref,
productusage.productusageaisleref,
productusage.productusageorder,
productusage.productusagecost,
productusage.productusagebuycount,
productusage.productusagefirstbuydate,
productusage.productusagelatestbuydate,
productusage.productusagerulesuggestflag,
productusage.productusagechecklistflag,
productusage.productusagechecklistcount,
/*********************************************************************************************************************************
Calculate the period in days from between the firstbuydate and the latestbuydate
*********************************************************************************************************************************/
(productusagelatestbuydate - productusagefirstbuydate) / (1000 * 60 * 60 * 24) AS periodindays,
products.productname,
aisles.aislename,
aisles.aisleorder,
aisles.aisleshopref,
shops.shopname,
shops.shopcity,
shops.shoporder,
rules._id AS rule_id,
rules.rulename,
rules.ruleuses,
rules.ruleprompt,
rules.ruleacton,
rules.ruleperiod,
rules.rulemultiplier
FROM productusage
LEFT JOIN products ON productusageproductref = products._id
LEFT JOIN aisles ON productusageaisleref = aisles._id
LEFT JOIN shops ON aisles.aisleshopref = shops._id
LEFT JOIN rules ON productusageaisleref = rules.ruleaisleref AND productusageproductref = rules.ruleproductref
WHERE productusagebuycount > 0 AND rules._id IS NULL AND (periodindays / 2) > 0 AND productusage.productusagebuycount > 0
public HashMap<String, String> get_update_invoice_getdata(String gen) {
// TODO Auto-generated method stub
HashMap<String, String> wordList;
wordList = new HashMap<String, String>();
Cursor cur_1 = ourDataBase
.rawQuery(
"SELECT * FROM Invoice i JOIN Client c ON i.Client_id=c.Client_id JOIN TAX t ON i.Tax_id=t.Tax_id JOIN Task it ON i.Task_id=it.Task_id WHERE i.Inv_no=?",
new String[] { gen });
int intext = cur_1.getColumnIndex(C_ORG_NAME);
int intext5 = cur_1.getColumnIndex(TA_NAME);
int intext6 = cur_1.getColumnIndex(TA_RATE);
int intext7 = cur_1.getColumnIndex(TA_QTY);
int intext8 = cur_1.getColumnIndex(TA_TOTAL);
if (cur_1.moveToFirst()) {
do {
wordList.put("Org_name", cur_1.getString(intext));
wordList.put("client_id", cur_1.getString(2));
wordList.put("po_number", cur_1.getString(4));
wordList.put("date", cur_1.getString(3));
wordList.put("dis_per", cur_1.getString(7));
wordList.put("item_name", cur_1.getString(intext5));
wordList.put("item_rate", cur_1.getString(intext6));
wordList.put("item_cost", cur_1.getString(intext7));
wordList.put("item_total", cur_1.getString(intext8));
} while (cur_1.moveToNext());
}
return wordList;
}
I am trying to have the user select a value, then search my database for that value, and return information from all rows containing that value.
I tried the following, but I am not sure how to return the results correctly to the other activity to be viewed by the user.
Cursor c = ourDatabase.rawQuery("SELECT * FROM FavoriteTable WHERE " + KEY_FAVNAME + " = '" + favoriteWorkoutName + "'", null);
I also tried this, where the int row is specifying the KEY_ROW I want to grab data from when querying. For example, the user enters the name of a favorite workout, it then searches the database for all rows containing that name, and then returns KEY_ROWS 1, 2, and 3 (which correspond to Exercise, Reps, etc). However, this only returns one row value from one row.
int row = 1;
Cursor c = ourDatabase.rawQuery("SELECT * FROM FavoriteTable WHERE " + KEY_FAVNAME + " = '" + favoriteWorkoutName + "'", null);
do{c.moveToNext();
String data = c.getString(row);
return data;
}while (row <= 3);
Any suggestions?
**EDIT:
I had already had something in my activity like that. Here is what I have there. However, I am getting an error, and it asks me to either "Change type of listCursor to Cursor", which it already is...or to "Change return type of 'GetFavoriteData' to Cursor", which is also already is.
Also, when I do get that to not have an error, I'm not sure how to use that returned data and insert it into my TextView. It will not allow me to setText(listCursor).
I ideally need to get the information from each returned row as a separate String so that I can display them the way I need to in TextViews.
In my Activity:
String favoriteWorkoutName = cfdName.getText().toString();
ExerciseDatabase choosefavorite = new ExerciseDatabase(WorkMeOutActivity.this);
try {
choosefavorite.open();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Cursor listCursor = choosefavorite.GetFavoriteData(favoriteWorkoutName);
choosefavorite.close();
in my DBHelper class:
public Cursor GetFavoriteData(String favoriteWorkoutName){
return ourDatabase.rawQuery("SELECT * FROM FavoriteTable WHERE " + KEY_FAVNAME + " = '" + favoriteWorkoutName + "'", null);
}
I think you misunderstood the getString() method. Have a look:
public abstract String getString (int columnIndex)
Since: API Level 1
Returns the value of the requested column as a String.
The result and whether this method throws an exception when the column value is null or the column type is not a string type is implementation-defined.
Parameters
columnIndex the zero-based index of the target column.
Returns
the value of that column as a String.
So, if you pass 1,2 or 3 inside with c.getString(row) it only gives you value of only one column(with index 1,2 or 3) from a specific row.
You can use c.getString(c.getColumnIndex(column_name)) if you are not sure about index of a column and want to get it from it's name.
Now I think you want to retrieve value of some columns(say first 3 column value of a row) from first few matching rows(like 3 rows) and use them if so use:
Cursor c = ourDatabase.rawQuery("SELECT * FROM FavoriteTable WHERE " + KEY_FAVNAME + " = '" + favoriteWorkoutName + "'", null);
String fcv="";
String scv="";
String tcv="";
if(c.getCount() == 0)
{
//no data found
}
else {
int i=0;
c.moveToFirst();
do {
fcv = fcv + c.getString(0)); //or use fcv = fcv + c.getString(c.getColumnIndex(name of first column)));
scv = fcv + c.getString(1));
tcv = fcv + c.getString(2));
i++;
} while (c.moveToNext() && i<3);
c.close();
Use the value of the strings as you like according to your need :)
Your question is not clear...
If you are trying to search all your different columns for that value your query should look more like this:
Cursor c = ourDatabase.rawQuery("SELECT * FROM FavoriteTable WHERE " + KEY_FAVNAME +
" LIKE '" + favoriteWorkoutName + "' OR " + COLUMN2 + " LIKE '" + favoriteWorkoutName +
"' OR " + COLUMN3 + " LIKE '" + favoriteWorkoutName + "'", null);
That will search columns KEY_FAVNAME, COLUMN2 and COLUMN3 and return any rows that contain favoriteWorkoutName's contents.
If you want only those from the specific column, what you first posted is correct.
EDIT
After re-reading, I'm getting a different idea of what your issue is.
You should call for the cursor in the activity in which you are going to use it. The normal method is to define a DB helper class that creates the db and contains all functions related to it (adding, deleting, updating info, getting cursors, etc). Then you instantiate that class and use it's methods to work with the db.
An example would be (used in the activity where you want to access the data):
mDbHelper = new WorkoutDB(getActivity()); // instantiate your dbhelper class
mDbHelper.open(); // use the dbhelper open method to open the db
Cursor listCursor = mDbHelper.fetchFavoriteWorkout(favoriteWorkoutName); // use the fetchFavoriteWorkout method to get your cursor
Your dbhelper class would contain a method (among many others) something like this:
public Cursor fetchFavoriteWorkout(String favoriteWorkoutName){
return ourDatabase.rawQuery("SELECT * FROM FavoriteTable WHERE " + KEY_FAVNAME + " = '" + favoriteWorkoutName + "'", null);
}
Background
I have an Android project that has a database with two tables: tbl_question and tbl_alternative.
To populate the views with questions and alternatives I am using cursors. There are no problems in getting the data I need until I try to join the two tables.
Tbl_question
-------------
_id
question
categoryid
Tbl_alternative
---------------
_id
questionid
categoryid
alternative
I want something like the following:
SELECT tbl_question.question, tbl_alternative.alternative where
categoryid=tbl_alternative.categoryid AND tbl_question._id =
tbl_alternative.questionid.`
This is my attempt:
public Cursor getAlternative(long categoryid) {
String[] columns = new String[] { KEY_Q_ID, KEY_IMAGE, KEY_QUESTION, KEY_ALT, KEY_QID};
String whereClause = KEY_CATEGORYID + "=" + categoryid +" AND "+ KEY_Q_ID +"="+ KEY_QID;
Cursor cursor = mDb.query(true, DBTABLE_QUESTION + " INNER JOIN "+ DBTABLE_ALTERNATIVE, columns, whereClause, null, null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
}
return cursor;
I find this way to form queries harder than regular SQL, but have gotten the advice to use this way since it is less error prone.
Question
How do I join two SQLite tables in my application?
You need rawQuery method.
Example:
private final String MY_QUERY = "SELECT * FROM table_a a INNER JOIN table_b b ON a.id=b.other_id WHERE b.property_id=?";
db.rawQuery(MY_QUERY, new String[]{String.valueOf(propertyId)});
Use ? bindings instead of putting values into raw sql query.
An alternate way is to construct a view which is then queried just like a table.
In many database managers using a view can result in better performance.
CREATE VIEW xyz SELECT q.question, a.alternative
FROM tbl_question AS q, tbl_alternative AS a
WHERE q.categoryid = a.categoryid
AND q._id = a.questionid;
This is from memory so there may be some syntactic issues.
http://www.sqlite.org/lang_createview.html
I mention this approach because then you can use SQLiteQueryBuilder with the view as you implied that it was preferred.
In addition to #pawelzieba's answer, which definitely is correct, to join two tables, while you can use an INNER JOIN like this
SELECT * FROM expense INNER JOIN refuel
ON exp_id = expense_id
WHERE refuel_id = 1
via raw query like this -
String rawQuery = "SELECT * FROM " + RefuelTable.TABLE_NAME + " INNER JOIN " + ExpenseTable.TABLE_NAME
+ " ON " + RefuelTable.EXP_ID + " = " + ExpenseTable.ID
+ " WHERE " + RefuelTable.ID + " = " + id;
Cursor c = db.rawQuery(
rawQuery,
null
);
because of SQLite's backward compatible support of the primitive way of querying, we turn that command into this -
SELECT *
FROM expense, refuel
WHERE exp_id = expense_id AND refuel_id = 1
and hence be able to take advanatage of the SQLiteDatabase.query() helper method
Cursor c = db.query(
RefuelTable.TABLE_NAME + " , " + ExpenseTable.TABLE_NAME,
Utils.concat(RefuelTable.PROJECTION, ExpenseTable.PROJECTION),
RefuelTable.EXP_ID + " = " + ExpenseTable.ID + " AND " + RefuelTable.ID + " = " + id,
null,
null,
null,
null
);
For a detailed blog post check this
http://blog.championswimmer.in/2015/12/doing-a-table-join-in-android-without-using-rawquery
"Ambiguous column" usually means that the same column name appears in at least two tables; the database engine can't tell which one you want. Use full table names or table aliases to remove the ambiguity.
Here's an example I happened to have in my editor. It's from someone else's problem, but should make sense anyway.
select P.*
from product_has_image P
inner join highest_priority_images H
on (H.id_product = P.id_product and H.priority = p.priority)