I am running a query in sqllite browser which gives the output fine.
But when i am running the query through Android's SqlLiteDatabase's rawQuery function it returns 3 rows for every original row
The query being -
select s.shout_id, u.user_id, u.image_id , u.firstname, u.lastname, s.shout_msg , i.user_id 'r_user_id' , i.image_id 'r_img_id', i.firstname 'r_firstname', i.lastname 'r_lastname', s.rec_time from shouts s left join users u
on s.user_id = u.user_id left join users i on s.reciever_id = i.user_id
where ((s.user_id =1 and s.reciever_id =2) or (s.user_id =2 and s.reciever_id =1) ) and s.shout_id < 5 order by s.shout_id desc limit 20;
The raw query code being in android -
public Cursor get_friend_shouts_less(String myid, String friend_id, long shout_id) {
open();
Log.d("ListView1", "get less Message myid friend_id shout_id: " + myid + " " + friend_id + " " + shout_id );
Cursor c = db.rawQuery("select s.shout_id, u.user_id, u.image_id , u.firstname, u.lastname, s.shout_msg , i.user_id 'r_user_id' , i.image_id 'r_img_id', i.firstname 'r_firstname', i.lastname 'r_lastname', s.rec_time from shouts s left join users u " +
"on s.user_id = u.user_id left join users i on s.reciever_id = i.user_id" +
" where ((s.user_id =? and s.reciever_id =?) or (s.user_id =? and s.reciever_id =?) ) and s.shout_id < ? order by s.shout_id desc limit 60;", new String[]{myid, friend_id, friend_id, myid , String.valueOf(shout_id)});
return c;
}
Glad it worked, now my official Answer:
A SQL LEFT JOIN Operation is equal to an karthesian product AxB, which can lead to rows wih the same content. In "simple": using GROUP BY merges those identical rows.
Related
I am working with sqlite where i have fired a query with "IN" operator but its taking to much time to display data. My query is following :
String Sql_query = "select id as _id,PARTY_NAME, PARTY_ADD1 as PARTY_ADD1,PARTY_KEY,PARTY_VAT from PARTYMAST where PARTY_KEY in( Select OUTACCD from OUTLETMST where OUTSMCD = " + salesman_code + " and Party_name like '" + etsearch.getText() + "%')";
Now to reducing time to get data i have made two cursors like below :
String Sql = "select id as _id,PARTY_NAME, PARTY_ADD1 as PARTY_ADD1,PARTY_KEY,PARTY_VAT from PARTYMAST";
String Sql2 = "Select OUTACCD from OUTLETMST where OUTSMCD = " + salesman_code + " and Party_name like '" + etsearch.getText() + "%')";
Cursor c1 = dbhelper.showdata(this,Sql);
Cursor c2 = dbhelper.showdata(this,Sql2);
Now i want to merge this two cursors c1 and c1 in one cursor like c1 + c2 = main_cursor
I have merged both cursor like following code :
MergeCursor merge_cursor = new MergeCursor(new Cursor[] {
c1, c2});
But it not worked in merge_cursor i getting only data of first cursor (c1).
Is it possible ? Please guide if anybody face this type of proble
Any other way to write this query ?
Thank you in advance.
You can use Join or Union for it.
Like for an example :-
sql = sql1;
sql += " UNION "
sql += sql2;
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've got a rather interesting situation. I have a SQLite database full of addresses and messages (addresses are not unique; messages are). Each message also has a date associated with it. What I want to do is select the first message's address, message, date, and how many messages are associated with the address.
So, I thought, "I can just GROUP by the address to only get one message per address, then ORDER these by the date, and also fetch the COUNT of the address column."
I did that, and it works... kinda. It fetches the correct count, fetches only one message per address, and orders them by date--but it does not select the most recent message for the address. It appears to be arbitrary.
As an example, I have three messages (earliest to latest) A, B, C from address Y, and three messages D, E, F from address Z. The query may fetch messages B and E, then sort them by date. It should fetch messages C and F, and sort those by date.
Here is what I have so far:
// Expanded version:
Cursor cursor = db.query(
/* FROM */ "messages_database",
/* SELECT */ new String[]{ "*", "COUNT(address) AS count" },
/* WHERE */ null,
/* WHERE args */ null,
/* GROUP BY */ "address",
/* HAVING */ null,
/* ORDER BY */ "date DESC"
);
// Or, same code on one line:
Cursor cursor = db.query("messages_database", new String[]{ "*", "COUNT(address) AS count" }, null, null, "address", null, "date DESC");
I feel like this may have to do with the HAVING clause, but I really don't know. I've used MySQL a lot with PHP, but never had to touch HAVING before. I tried setting my HAVING clause to "MAX(date)", but it had no effect. If I set my GROUP BY clause to be "address, date", then they are sorted by date, but of course they are all individual instead of grouped (since the dates differ).
Google searches have proved fruitless; queries like "android sqlite order before group" and "android sqlite group by order" yield no related results.
How can I select the one latest message for each address without removing my GROUP clause (as COUNT() relies upon this)? Do I need two queries?
Edit: Based on the answer #Adrian linked me to in the comments, I came up with two queries which both produced the same result; one row, in which the count was 7 (which is the total number of addresses, not messages per address), and the address shown was not that of the latest message.
The two queries were:
Cursor cursor = db.rawQuery(
"SELECT t.*, COUNT(t.message_content) AS count "
+ "FROM messages_database t "
+ "INNER JOIN ("
+ " SELECT address, MAX(date) AS maxdate "
+ " FROM messages_database "
+ " GROUP BY address "
+ ") ss ON t.address = ss.address AND t.date = ss.maxdate",
null
);
Cursor cursor = db.rawQuery(
"SELECT t1.*, COUNT(t1.message_content) AS count "
+ "FROM messages_database t1 "
+ "LEFT OUTER JOIN messages_database t2 "
+ "ON (t1.address = t2.address AND t1.date < t2.date) "
+ "WHERE t2.address IS NULL",
null
);
SQLite has an extension that makes greatest-n-per-group problems much easier:
If you are using the MAX() or MIN() aggregate functions, and if you are selecting other columns at the same time without using them in an aggregate function or grouping by them, then the resulting values for those columns are guaranteed to come out of the same record that has the maximum/minimum value. (This is not allowed in other SQL dialects, and was introduced in SQLite 3.7.11.)
So, for your problem, you can use a query like this:
SELECT *, COUNT(address) AS count, MAX(date)
FROM messages_database
GROUP BY address
If you don't have SQLite 3.7.11 (which is likely on most Android versions) or using another SQL engine, the following query will work:
SELECT *,
(SELECT COUNT(address) AS count
FROM messages_database m2
WHERE m1.address = m2.address)
FROM messages_database m1
WHERE date = (SELECT MAX(date)
FROM messages_database m3
WHERE m1.address = m3.address)
GROUP BY address
Solved it! I ended up using a combination of #CL.'s method and the methods I outlined in my edited post (clarified in this answer, posted by #Adrian).
Because I didn't want to use 3 SELECT statements (as #CL.'s answer described), I used the same INNER JOIN concept as in the other statements, while retaining his methodology.
The result is this:
Cursor cursor = db.rawQuery(
"SELECT t.*, ss.count AS count "
+ "FROM messages_database t "
+ "INNER JOIN ("
+ " SELECT address, MAX(date) AS maxdate, COUNT(address) AS count "
+ " FROM messages_database "
+ " GROUP BY address "
+ ") ss ON t.address = ss.address AND t.date = ss.maxdate "
+ "GROUP BY t.address "
+ "ORDER BY t.date DESC ",
null
);
And it's working perfectly!
Hi, I am new to android. I want to write query for join in sqlite. My code is -
public Cursor SearchCategory(SQLiteDatabase db){
//return db.query("category_master", null, "status = 'Active'", null, null, null, null);
String Category_Sql = " select category_master.*,count(*) as cnt from product_master " +
" left join category_master on product_master.category_id = category_master.category_id " +
" where category_master.status = 'Active' group by category_master.category_id having cnt > 0 ";
return db.query(Category_Sql);
}
but it generate error. Where am I wrong?
It would have helped more if you could cite the error description.
Try running the query first in Portable SQLite explorer
you can use rawQuery
db.rawQuery("Select a.column1,b.column1 FROM table1 a JOIN table2 b ON a._id=b._id", null);
it should work
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)