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.
Related
For my application, I need to query a sqlite database around 40-50 times. I am sure that the code I wrote is very inefficient. Unfortunately, I cannot find many examples online that involves querying the database many times.
String[] entryValArray = new String[indicesList.size()];
DBHelper dbHelper = new DBHelper(MainActivity.context);
SQLiteDatabase db = dbHelper.getReadableDatabase();
for (int i = 0; i < indicesList.size(); i++) {
int moddedIndex = Integer.parseInt(indicesList.get(i), 16) % DBHelper.numEntries;
String queryStr = "select * from " + DBHelper.TBL_NAME + " where " + DBHelper.IDStr +
" = " + Integer.toString(moddedIndex);
Cursor cursor = db.rawQuery(queryStr, null);
if (cursor.moveToFirst())
entryValArray[i] = cursor.getString(1);
cursor.close();
}
Basically, I am taking a list of strings, converting them to hex values, and then modding the value to get an index into a sqlite database. This is for a password generator application.
Is there a better way to do this, especially regarding creating a cursor and then closing it in every iteration.
First of all you have to change your query string as you need only one column value but you are using
Select *
instead of
Select yourColumn
. Secondly if your indices list size is not very large you can use
IN(values ) function of db instead of
" where " + DBHelper.IDStr +" = " + Integer.toString(moddedIndex);
this will return the result in only one query you don't have to run a whole loop.
I have a simple insert SQLite statement :
private final String INSERT_FEED = "insert into "
+ TABLE_FEEDS
+ "([id],[first_name], [last_name], [image], [content]) "
+ "values (?,?,?,?,?)";
Which i used in a method :
public long insertFeed(FeedsModel feed) {
this.insertStmt = db.compileStatement(INSERT_FEED);
this.insertStmt.bindString(1, feed.getId() + "");
this.insertStmt.bindString(2, feed.getFirstName());
this.insertStmt.bindString(3, feed.getLastName());
this.insertStmt.bindString(4, feed.getProfileImage());
this.insertStmt.bindString(5, feed.getContentDetails());
long result = this.insertStmt.executeInsert();
return result;
}
Later, i used that method in my activity :
for(int i = 0; i < feedsAdapter.getCount() - 1; i++)
{
helper.insertFeed(feedsAdapter.getItem(i));
}
The problem is, the table always have only 1 row because the new data will overwrite the existing row, instead of inserted in a new row.
Your current for loop is inserting all the FeedsModels other than the last one. If you only have one model in the list it would explain why you are not seeing any new inserts into the table, instead do:
for (FeedsModel feedsModel : feedsAdapter) {
helper.insertFeed(feedsModel);
}
I have the table(shown in fig). I want to update the time column. I have the medicine_id(second column) how to update the different times depend upon the same medicine_id(second column).
Also, I have four rows in my DB when I updated time it should be two row means, I want to remove the other two rows with the particular medicine_id..
I have the following code for update:
if(mon) {
for (int i = 0; i < dosage_per_day; i++) {
mMedicineDbHelper.updateMedicines(new
MedicineTime(row_id, time_InMillies[i]));
}
}
My DB code:
public void updateMedicines(MedicineTime medicineTime) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ID, medicineTime.getId());
values.put(MEDICINEID, medicineTime.getMedicine_id());
values.put(TIME, medicineTime.getTime_in_millis());
db.update(TABLENAME, values, MEDICINEID + " = ?",
new String[]{String.valueOf(medicineTime.getMedicine_id())});
db.close();
}
It shows exception..
anybody help to solve this...
For the original question asked you're executing the following code:
db.update(TABLENAME, values, MEDICINEID + " = ?",
new String[]{String.valueOf(medicineTime.getMedicine_id())});
For some reason on Android, converting numeric values to Strings and using them for selection args never works properly. See: Android Sqlite selection args[] with int values. The best way to do this is the following:
db.update(TABLENAME, values, MEDICINEID + " = " + medicineTime.getMedicine_id(), null);
I'm not sure what I'm doing wrong, but I'm trying to update a single integer value in a column of a table to 1 from 0. When creating the database, I set all values of the column to zero using:
for (int i = 0; i < setups.length; i++) {
ContentValues values = new ContentValues();
values.put(JokeDbContract.TblJoke.COLUMN_NAME_SETUP, setups[i]);
values.put(JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE, punchlines[i]);
values.put(JokeDbContract.TblJoke.COLUMN_NAME_USED, 0);
db.insert(JokeDbContract.TblJoke.TABLE_NAME, null, values);
}
Then, in the actual activity, I'm doing:
private void findNewJoke() {
JokeDb jokeDb = JokeDb.getInstance(this);
SQLiteDatabase theDb = jokeDb.getDB();
String selection = JokeDbContract.TblJoke.COLUMN_NAME_USED + "=" + 0;
// Query database for a joke that has not been used, update the fields
// theJoke and thePunchline appropriately
String[] columns = {JokeDbContract.TblJoke._ID,
JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE,
JokeDbContract.TblJoke.COLUMN_NAME_SETUP,
JokeDbContract.TblJoke.COLUMN_NAME_USED};
Cursor c = theDb.query(JokeDbContract.TblJoke.TABLE_NAME, columns, selection,
null, null, null, null);
if (c.moveToFirst() == false) {
Toast.makeText(this, R.string.error_retrieving_joke, Toast.LENGTH_LONG).show();
Log.e(getString(R.string.app_name),"No jokes retreived from DB in JokeActivity.findNewJoke()!");
}
else {
ContentValues values = new ContentValues();
theSetup = c.getString(c.getColumnIndexOrThrow(JokeDbContract.TblJoke.COLUMN_NAME_SETUP));
thePunchline = c.getString(c.getColumnIndexOrThrow(JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE));
String updateSelection = JokeDbContract.TblJoke.COLUMN_NAME_SETUP + "=" + theSetup;
values.put(JokeDbContract.TblJoke.COLUMN_NAME_USED, 1);
theDb.update(JokeDbContract.TblJoke.TABLE_NAME, values, updateSelection, null);
}
}
I'm getting an error on the update:
java.lang.RuntimeException: .... while compiling: UPDATE jokes SET used=?
WHERE setup=Why do programmers always mix up Halloween and Christmas?
It seems as though I'm not getting an actual value set for the used column. What the program ultimately does is cycle through jokes where used=0, then sets used to 1 when it has been viewed. So the query only pulls those jokes that aren't used yet. I have a feeling I'm missing something simple, one can hope.
I think you are having problems with quotation marks.
Example:
String updateSelection = JokeDbContract.TblJoke.COLUMN_NAME_SETUP + "=\"" + theSetup + "\"";
However, the recommended way to do this, would be:
theDb.update(JokeDbContract.TblJoke.TABLE_NAME, values, JokeDbContract.TblJoke.COLUMN_NAME_SETUP + " = ?", new String[] { theSetup });
It is better to use field = ?, because this helps sqlite cache queries (I believe).
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();
}