I have a table with events, and want to select all events which happen from three days ago. (The events do belong to categories which is a field in the Article table which has to description etc., but that works).
This is my code:
public List<Event> findByCategory(long catId, int start, int count) throws Exception {
String limit = "";
if (count > 0) limit = start + "," + count;
SQLiteQueryBuilder _QB = new SQLiteQueryBuilder();
_QB.setTables("Event e INNER JOIN Article a ON e.articleId=a.id");
String[] rows = _fields.keySet().toArray(new String[0]);
for (int i = 0; i < rows.length; i++) {
rows[i] = "e." + rows[i];
}
Cursor cursor = _QB.query(_db, rows, "date(e.startTime) > date('now','-3 days') AND EXISTS(SELECT * FROM CategoryArticleLink WHERE CategoryArticleLink.articleId = a.id AND EXISTS (SELECT * FROM Category WHERE Category.id = CategoryArticleLink.categoryId AND Category.id = '" + catId + "'))", null, null, null, "e.startTime ASC", limit);
List<Event> list = new ArrayList<>(cursor.getCount());
if (cursor.moveToFirst()) {
do {
Event item = getObject(cursor);
list.add(item);
} while (cursor.moveToNext());
}
cursor.close();
return list;
}
The resulting Query is:
SQLiteQuery: SELECT e.articleId, e.keynote, e.locationId, e.id, e.finishTime, e.startTime, e.languageCode, e.flag, e.lastUpdate FROM Event e INNER JOIN Article a ON e.articleId=a.id WHERE (date(e.startTime) > date('now','-3 days') AND EXISTS(SELECT * FROM CategoryArticleLink WHERE CategoryArticleLink.articleId = a.id AND EXISTS (SELECT * FROM Category WHERE Category.id = CategoryArticleLink.categoryId AND Category.id = '9'))) ORDER BY e.startTime ASC LIMIT 0,8
This returns an empty result set. However, if I take the date selection (date(e.startTime) > date('now','-3 days')) out of the query it returns all records as expected. So I must be doing something small wrong, but I just don't see what.
The field type on database generation is 'DATE', so that should be ok I expect.
The correct code in my setup is 'date(e.startTime / 1000, 'unixepoch') > date('now','-3 day')'. I use Date().getTime() to save the date/time as a long. But have uses a timestamp a 1000x larger then normal.
Moreover, the 'unixepoch' seems to be a needed addition in the date function.
Related
So I have a filled in Database with the columns: _ID, excersise, reps and timestamp. Im trying to print out the row with the highest rep number of an excersise with this Cursor:
private Cursor getRepRecord(String excersise) {
return myDatabase.query(Contact.ExcersiseEntry.TABLE_NAME,
new String [] {"MAX(reps)"},
Contact.ExcersiseEntry.EXCERSISE_NAME + "= '" + excersise + "'",
null,
null,
null,
Contact.ExcersiseEntry.EXCERSISE_REPS + " DESC");
}
and then I use this method to print the cursor rows:
private void getEntryFromDatabase(Cursor cursor) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
String excersise = cursor.getString(cursor.getColumnIndex(Contact.ExcersiseEntry.EXCERSISE_NAME));
int reps = cursor.getInt(cursor.getColumnIndex(Contact.ExcersiseEntry.EXCERSISE_REPS));
int id = cursor.getInt(cursor.getColumnIndex(Contact.ExcersiseEntry._ID));
Log.i("Entry", "ID: " +id + " || Excersise: " + excersise + " || reps: " + Integer.toString(reps));
cursor.moveToNext();
}
}
How ever I get the Error: CursorWindow: Failed to read row 0, column -1 from a CursorWindow which has 1 rows, 1 columns. I know there are alot of similar questions but I looked at man and still couldn´t find the Solution...
The reason why you are getting the -1 is because the columns you are trying to extract data from do not exist in the Cursor (the getColumnIndex method returns -1 if the column cannot be found).
The Cursor will only have a single column named MAX(reps).
You can easily add all the other columns by adding * (separated from the MAX(reps) column by a comma or you could add other columns individually as elements of the array. If you want to display the maximum reps you would extract the column named MAX(reps) or you could rename the column using AS e.g. MAX(reps) as maxreps
So you could have :-
private Cursor getRepRecord(String excersise) {
return myDatabase.query(Contact.ExcersiseEntry.TABLE_NAME,
new String [] {"MAX(reps) AS maxreps", *}, //<<<< Changed
Contact.ExcersiseEntry.EXCERSISE_NAME + " = '" + excersise + "'",
null,
null,
null,
Contact.ExcersiseEntry.EXCERSISE_REPS + " DESC");
}
This could be used in conjunction with a slightly amended getEntryFromDatabase method :-
private void getEntryFromDatabase(Cursor cursor) {
//cursor.moveToFirst(); //<<< does nothing of any use as return value is ignored
while (!cursor.isAfterLast()) {
String excersise = cursor.getString(cursor.getColumnIndex(Contact.ExcersiseEntry.EXCERSISE_NAME));
int reps = cursor.getInt(cursor.getColumnIndex(Contact.ExcersiseEntry.EXCERSISE_REPS)); // Would this be of any use???
int id = cursor.getInt(cursor.getColumnIndex(Contact.ExcersiseEntry._ID));
int maxreps = cursor.getInt(cursor.getColumnIndex("maxreps")); //<<<< Added
Log.i("Entry", "ID: " +id + " || Excersise: " + excersise + " || reps: " + Integer.toString(reps) + " || maxreps: " + Integer.toString(maxreps);
cursor.moveToNext();
}
}
EDIT re comment :-
I still don´t quite understand why. The correct SQL term would be
something like SELECT * WHERE reps = max(reps), right? How does it
translate into the Max(reps), *
If you used SELECT * FROM reps WHERE reps = Max(reps) it would return all defined columns (the * translates to all columns) for the row or rows that is/are equal to highest rep value (note see below why this would work anyway). Which could be what you want. (ORDER BY reps DESC (or ASC) is irrelevant).
The list of columns after SELECT (SELECT ALL or SELECT DISTINCT) defined the result_columns i.e. the columns that will exist in the resultant Cursor. If you said SELECT reps FROM reps then the resultant cursor would have just the 1 column called reps. SELECT reps, exercise then the resultant cursor would have two columns.
SQL allows derived columns (my term). The derived column name will take the name of the expression used to derive the value. So if you say SELECT max(reps) FROM reps then the result will be a Cursor with 1 column named max(reps) (and because MAX is an aggregate function 1 row (unless GROUP BY is used)).
The query method used (there are 4 in total) in your code has the signature :-
Cursor query (String table,
String[] columns, //<<<< list of result columns
String selection, //<<<< WHERE CLAUSE
String[] selectionArgs,
String groupBy,
String having,
String orderBy)
So :-
myDatabase.query(Contact.ExcersiseEntry.TABLE_NAME,
new String [] {"MAX(reps)"},
Contact.ExcersiseEntry.EXCERSISE_NAME + "= '" + excersise + "'",
null,
null,
null,
Contact.ExcersiseEntry.EXCERSISE_REPS + " DESC");
results in the SQL SELECT MAX(reps) FROM reps WHERE excercise = 'your_excercise';
So the resultant Cursor will have 1 column named MAX(reps).
If you wanted SELECT * FROM reps WHERE reps = MAX(reps) then you'd use :-
myDatabase.query(Contact.ExcersiseEntry.TABLE_NAME,
null, //<<<< ALL columns
Contact.ExcersiseEntry.EXCERSISE_REPS + " = MAX(reps)",
null,
null,
null,
Contact.ExcersiseEntry.EXCERSISE_REPS + " DESC" // Irrelevant
);
However, this would be for all Exercises and could thus return multiple rows BUT it would be a misuse of an aggregate function (attempt apply the function on a per row basis as opposed to on a per group basis (all rows are the group as no GROUP BY has been specified)).
You'd have to use a subquery e.g. SELECT * FROM reps WHERE reps = (SELECT MAX(reps) FROM reps)
In my android app I have an SQLite database. with this structure:
PrivateList(idList INTEGER PRIMARY KEY, name TEXT, creationDate TEXT, active INTEGER, deactivationDate TEXT);
PrivateProduct (idProduct INTEGER PRIMARY KEY, description TEXT, quantity INTEGER, active INTEGER, additionDate TEXT);
List_Product(idList INTEGER NOT NULL, idProduct INTEGER NOT NULL, PRIMARY KEY (idList, idProduct), FOREIGN KEY(idList) REFERENCES PrivateList(idList), FOREIGN KEY(idProduct) REFERENCES PrivateProduct(idProduct));
I have an autogenerator list and elements using for to try the app:
localDB = new LocalDB(this, "localBD", null, 1);
SQLiteDatabase sqLiteDatabase = localDB.getWritableDatabase();
if (sqLiteDatabase != null){
for (int i = 0; i < 10; i++){
String a = "List" + i;
String b = "Product" + i;
Log.i("execfor", "INSERT INTO PrivateList (name, creationDate, active, deactivationDate) " + " VALUES ('" + a + "', CURRENT_TIMESTAMP, 1, null);");
sqLiteDatabase.execSQL("INSERT INTO PrivateList (name, creationDate, active, deactivationDate) " + " VALUES ('" + a + "', CURRENT_TIMESTAMP, 1, null);");
sqLiteDatabase.execSQL("INSERT INTO PrivateProduct (description, quantity, active, additionDate) " + " VALUES ('" + b + "', 3, 1, CURRENT_TIMESTAMP);");
//sqLiteDatabase.execSQL("INSERT INTO List_Product (idList, idProduct) values ");//
}
}
But I can`t find the way to get rowIds from each list and product to insert both, idlist and idproduct, into List_Product.
Thank you in advance.
The main change to facilitate grabbing the id's would be to swap from using execSQL to using insert as insert returns the id of the inserted row, execsql does not.
A little more on this here Using execSQL for INSERT operation in Android SQLite.
However, I'm not sure if you can pass CURRENT_TIMESTAMP via a ContentValues and it would result getting the current timestamp as opposed to just setting the value to the literal CURRENT_TIMESTAMP. You could use DEFAULT CURRENT_TIMEPSTAMP in the respective column definitions (as I have in the code below).
I'd suggest that you would not want a link between every list/product permutation (that would be 100 rows for you 10 List rows and 10 Product rows) as in real life you would probably not have such a scenario rather you'd have some links between the two. So in the code below I've randomly created links.
First some code from the Database Helper (for my convenience named SO45449914) for performing the inserts:-
public long insertListRow(String name,
int active) {
ContentValues cv = new ContentValues();
cv.put(LISTNAME_COL,name);
cv.put(LISTACTIVE_COL,active);
cv.put(LISTDEACTIVATIONDATE_COL,"");
return this.getWritableDatabase().insert(PRIVATELISTTABLE,null,cv);
}
public long insertProductRow(String description,int quantity, int active) {
ContentValues cv = new ContentValues();
cv.put(PRODUCTDESCRIPTION_COL,description);
cv.put(PRODUCTQUANTITY_COL,quantity);
cv.put(PRODUCTACTIVE_COL,active);
return this.getWritableDatabase().insert(PRIVATEPRODUCTTABLE,null,cv);
}
public void insertListProductLink(long listid, long productid) {
ContentValues cv = new ContentValues();
cv.put(LISTPRODUCTLIST_COL,listid);
cv.put(LISTPRODUCTPRODUCT_COL,productid);
if (this.getWritableDatabase().insertOrThrow(LISTPRODUCTTABLE,null,cv) <0) {
//handle failed insert
}
}
Notes
- I've used class variables for all columns names.
- Columns that have the current time stamp get this via the default, so there is no need to have a cv.put for those columns.
In the activity is the following code :-
void doSO45449914() {
SO45449914 dbhelper = new SO45449914(this);
int loopcount = 10;
long[] listids = new long[loopcount];
long[] productids = new long [loopcount];
for (int i=0; i < 10; i++) {
listids[i] = dbhelper.insertListRow("a" + i,1);
productids[i] = dbhelper.insertProductRow("b" + i,3,1);
}
Cursor csra = dbhelper.getWritableDatabase().query(SO45449914.PRIVATELISTTABLE,
null,null,null,null,null,null
);
Cursor csrb = dbhelper.getWritableDatabase().query(SO45449914.PRIVATEPRODUCTTABLE,
null,null,null,null,null,null
);
Log.d("SO45449914","Number of rows in LIST TABLE = " + csra.getCount());
Log.d("SO45449914","Number of rows in PRODUCTS TABLE = " + csrb.getCount());
for (long aid: listids) {
Log.d("SO45449914","LIST ID from store = " + Long.toString(aid));
}
for (long bid: productids) {
Log.d("SO45449914","PRODUCT ID from store = " + Long.toString(bid));
}
for (long lid: listids) {
for (long prdid: productids) {
if ((Math.random() * 100) > 60) {
dbhelper.insertListProductLink(lid,prdid);
Log.d("SO45449914",
"Adding link between List id(" +
Long.toString(lid) +
") and product id(" +
Long.toString(prdid) +
")"
);
}
}
}
csra.close();
csrb.close();
}
Exlapnation
The first few lines prepare long arrays based upon the number of Lists and products to be created (same number of both). Integer loopcount determines how many.
The first loop, inserts Lists and Products which use the insert method storing the returned id in the respective array element.
Two Cursors are then created for obtaining row counts, which are then written to the log. The id's as stored in the arrays are output to the log.
Two nested loops are then invoked with Products being the inner (not that it matters) and randomly (about 40% of the time) a row will be inserted into the link table. I've assumed random but you always easily adjust the algorithm to follow a pattern. It's if ((Math.random() * 100) > 60) { that determines whether or not to insert a link.
The two Cursors are then closed.
Results
Here are screen shots of the resultant tables :-
PrivateList Table
PrivateProduct Table
List_Product Table
..... (44 rows in the List_Product table)
Well, this is what I did. Despite of the fact that there is a way do the same without so many rows in List_Product table; I'd like to understand the way. (Also I had problem in the for so it didnt do what I wanted exactly).
localDB = new LocalDB(this, "localBD", null, 1);
SQLiteDatabase sqLiteDatabase = localDB.getWritableDatabase();
if (sqLiteDatabase != null){
long idList;
long idProduct;
for (int i = 0; i < 10; i++){
String a = "List" + i;
String b = "Product" + i;
ContentValues contentValuesList = new ContentValues();
contentValuesList.put("name", a);
contentValuesList.put("active", 1);
contentValuesList.put("creationDate", "CreationDate");
contentValuesList.put("deactivationDate", "");
idList = sqLiteDatabase.insert("PrivateList", null, contentValuesList);
for (int j = 0; j < 10; j++){
ContentValues contentValuesProduct = new ContentValues();
contentValuesProduct.put("description", b);
contentValuesProduct.put("active", 1);
contentValuesProduct.put("quantity", 1);
contentValuesProduct.put("additionDate", "additionDdate");
idProduct = sqLiteDatabase.insert("PrivateProduct", null, contentValuesProduct);
ContentValues contentValuesListProduct = new ContentValues();
contentValuesListProduct.put("idList", idList);
contentValuesListProduct.put("idProduct", idProduct);
sqLiteDatabase.insert("List_Product", null, contentValuesListProduct);
}
I know it could be more efficient, but that it doesn't matter now.
This is the result in the database:
PrivateList:
with 10 rows
PrivateProduct:
with 100 rows.
List_Product:
The problem was that I didn't know the existance of sqlLiteDatabase.insert(...) method.
Thanks you all.
I would like to implement a SQL-Query that gives me a range of entries. Example:
public List<Entry> getEntries(int lowerValue, int upperValue){
//Select upper - lower entries.
}
getEntries(0, 20) --> First 20 Entries
getEntries(21, 40) --> Entry 21 to 40
getEntries(12, 200) --> Entry 12 to 200
At the moment I get all entries like this:
public List<Log> getLogs(){
List<Log> list = new ArrayList<>();
SQLiteDatabase db = getWritableDatabase();
String query = "SELECT * FROM " + TABLE_LOGS + " WHERE 1";
//Cursor points to a location in your results
Cursor c = db.rawQuery(query, null);
//Move to the first row in your results
c.moveToFirst();
//Position after the last row means the end of the results
while (!c.isAfterLast()) {
if (c.getString(c.getColumnIndex("type")) != null) {
int id = c.getInt(c.getColumnIndex("_id"));
int type = c.getInt(c.getColumnIndex("type"));
long date = c.getLong(c.getColumnIndex("date"));
int refId = c.getInt(c.getColumnIndex("refId"));
String extra = c.getString(c.getColumnIndex("extra"));
list.add(new Log(id, type, date, refId, extra));
}
c.moveToNext();
}
db.close();
c.close();
return list;
}
I know there is a LIMIT clause, but that starts always from the first entry.
What you need is the combination of LIMIT and OFFSET
You could use
LIMIT <skip>, <count>
or
LIMIT <count> OFFSET <skip>
So some examples would be
"SELECT * FROM " + TABLE_LOGS + " WHERE type=1 LIMIT 20 OFFSET 0"
"SELECT * FROM " + TABLE_LOGS + " WHERE type=1 LIMIT 20 OFFSET 20"
"SELECT * FROM " + TABLE_LOGS + " WHERE type=1 LIMIT 20 OFFSET 40"
Hope this helps.
I'm trying to implement dynamic queries in my Android app, to let the users search according to some criteria. In this case I'm trying to search simply by an integer value. Here's my attempt:
...
public String[][] listarNegocio(int idProyecto,
int minimo,
int maximo)
{
String[][] arrayDatos = null;
String[] parametros = {String.valueOf(idProyecto)};
Cursor cursor = null;
cursor = querySQL("SELECT *" +
" FROM negocio" +
" WHERE ? in (0, id_proyecto)", parametros);
if(cursor.getCount() > 0)
{
int i = minimo - 1;
arrayDatos = new String[maximo - minimo + 1][20];
while(cursor.moveToNext() && i < maximo)
{
// Here I fill the array with data
i = i + 1;
}
}
cursor.close();
CloseDB();
return(arrayDatos);
}
public Cursor querySQL(String sql, String[] selectionArgs)
{
Cursor oRet = null;
// Opens the database object in "write" mode.
db = oDB.getReadableDatabase();
oRet = db.rawQuery(sql, selectionArgs);
return(oRet);
}
...
I tested this query using SQLFiddle, and it should return only the rows where the column id_proyecto equals the parameter idProyecto, or every row if idProyecto equals 0. But it doesn't return anything. If I remove the WHERE clause and replace "parametros" with "null", it works fine.
Additionally, I need to search by text values, using LIKE. For example, WHERE col_name LIKE strName + '%' OR strName = ''. How should I format my parameters and the query to make it work?
You should do one query for each case. For an id that exists, do SELECT * FROM negocio WHERE id_proyecto = ?. For an id that doesn't exist (I'm assuming 0 isn't a real id), just query everything with SELECT * FROM negocio.
Code should be something like this:
if(parametros[0] != 0){
cursor = querySQL("SELECT *" +
" FROM negocio" +
" WHERE id_proyecto = ?", parametros);
} else {
cursor = querySQL("SELECT *" +
" FROM negocio", null);
}
Regarding your second question, it depends on what you're looking for, you could use LIKE '%param%' or CONTAINS for occurrences in between text, LIKE param for partial matches or just = param if you're looking an exact match.
http://postimg.org/image/4vozphdk7/
So these are my tables;
I save my data in such a way that the Workout_ID (second figure) references the Workout_ID in the top figure.
What i need - How can i 'Look up' what Workout_ID 2 is and output the string, when it comes to displaying my table?
Done in SQLite, is this possible?
Coded Table column Names:
date_id = date_of_workout_id
date = date_of_workout
workout_name = workout_name
date_of_workout = DateofWorkout
workout_table = WorkoutTable
workout_id(date's one) = name_of_workout
workout_id(WorkoutTables's one) = workout_id
public String test(String WorkoutSelectedNameInfo) {
// TODO Auto-generated method stub
String Weight = "";
open();
ourDatabase = ourhelper.getReadableDatabase();
Cursor c = ourDatabase.rawQuery("SELECT date_of_workout_id,
date_of_workout, workout_name FROM DateofWorkout JOIN WorkoutTable ON
DateofWorkout.name_of_workout = WorkoutTable.workout_id", null);
int iWeight = c.getColumnIndex(KEY_WORKOUT_NAME);
while(c.moveToNext())
{
Weight = Weight + c.getString(iWeight) + "\n";
}
c.close();
ourDatabase.close();
System.out.println(Weight);
return Weight;
}
A very simple JOIN can do that;
SELECT date_id, date, workout_name
FROM date_of_workout
JOIN workout_table
ON date_of_workout.workout_id = workout_table.workout_id
SELECT date_ID, date, workout_name
FROM workouts, dates
WHERE workouts.workout_ID = dates.workout_ID
Updated Answer:
Try changing
Cursor c = ourDatabase.rawQuery("SELECT date_of_workout_id,
date_of_workout, workout_name FROM DateofWorkout JOIN WorkoutTable ON
DateofWorkout.name_of_workout = WorkoutTable.workout_id", null);
to
Cursor c = ourDatabase.rawQuery("SELECT date_of_workout_id,
date_of_workout, workout_name FROM DateofWorkout JOIN WorkoutTable ON
DateofWorkout.workout_id = WorkoutTable.workout_id", null);
the problem maybe is because you are comparing name_of_workout from workout_id.
A simple inner join perhaps?
select dow.date_id, dow.date, w.name
from workout w, date_of_workout dow
where w.workout_id = dow.workout_id