I'm trying to create a score database that increments the players 'score' by one when they win by calling updateScore(). The primary key and player number are identical (I may need to restructure the DB at some point) and the final column is 'score'.
Below is the code that initially sets the score (this works), the method that gets the score (also works fine) and the method that updates the score, incrementing the relevant players score by 1. This is the part the doesn't work, is there something I should be doing differently here? Thanks.
/** Add a record to the database of two player scores
* #param playerId
* #param playerScore
**/
public void addScore (int playerId, int playerScore) {
SQLiteDatabase database = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ID, playerId);
values.put(PLAYERNUM, playerId);
values.put(SCORE, playerScore);
database.insert(TABLE_2PSCORES, null, values);
database.close();
}
// Get the score
public int getScore (int playerId) {
SQLiteDatabase database = this.getReadableDatabase();
Cursor cursor = database.query(TABLE_2PSCORES, COLUMNS, " player = ?", new String[] {String.valueOf(playerId) }, null, null, null, null); //null = groupby, having, orderby, limit
if (cursor !=null) { cursor.moveToFirst(); }
int output = cursor.getInt(2);
return output;
}
// Increment score by 1
public void updateScore (int playerId) {
SQLiteDatabase database = this.getWritableDatabase();
int playerScore = getScore(playerId);
int playerScoreInc = playerScore ++;
ContentValues values = new ContentValues();
values.put("score", playerScoreInc);
database.update(TABLE_2PSCORES, values, PLAYERNUM+" = ?", new String[] {String.valueOf(playerId)} );
database.close();
}
int playerScoreInc = playerScore ++;
This assigns playerScore to playerScoreInc and only after that increments playerScore. To first increment and then assign, change to ++playerScore.
However, you can do it all in SQL, no need to fetch score, increment it in code and then update the database table separately:
database.execSQL("UPDATE " + TABLE_2PSCORES + " SET " + SCORE + "=" + SCORE + "+1" + " WHERE " + PLAYERNUM + "=?",
new String[] { String.valueOf(playerId) } );
The other answers solve the original question, but the syntax makes it hard to understand. This is a more general answer for future viewers.
How to increment a SQLite column value
SQLite
The general SQLite syntax is
UPDATE {Table} SET {Column} = {Column} + {Value} WHERE {Condition}
An example of this is
UPDATE Products SET Price = Price + 1 WHERE ProductID = 50
(Credits to this answer)
Android
Now that the general syntax is clear, let me translate that into Android syntax.
private static final String PRODUCTS_TABLE = "Products";
private static final String ID = "ProductID";
private static final String PRICE = "Price";
String valueToIncrementBy = "1";
String productId = "50";
String[] bindingArgs = new String[]{ valueToIncrementBy, productId };
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL("UPDATE " + PRODUCTS_TABLE +
" SET " + PRICE + " = " + PRICE + " + ?" +
" WHERE " + ID + " = ?",
bindingArgs);
db.close();
TODO
This answer should be updated to use update rather than execSQL. See comment below.
Change
int playerScoreInc = playerScore ++;
to
int playerScoreInc = ++ playerScore;
I think this will work
// Increment score by 1
public void updateScore (int playerId) {
SQLiteDatabase database = this.getWritableDatabase();
int playerScore = getScore(playerId);
int playerScoreInc = ++ playerScore;
ContentValues values = new ContentValues();
values.put("score", playerScoreInc);
database.update(TABLE_2PSCORES, values, PLAYERNUM+" = ?", new String[] {String.valueOf(playerId)} );
database.close();
}
Have you tried debugging? Try debugging this line:
int playerScoreInc = playerScore ++;
The playerScoreInc doesn't increment.
Related
I created a sqlite database to store the songs which are most played by the user. Basically, I'm creating "Most played songs" playlist for my music app. The logic I'm using is, when the user selects a song, it is send to the database. If is already there in the database, an integer field will increment its value else the song will be added to database. And I display the result according to the integer field(song with most integer value will be displayed on the top).But my app does't do the sorting according to the integer value. I don't know what is wrong.
Code to add songs:
public void addSong(SongInfoModel songInfoModel){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_ID, songInfoModel.getSongID());
values.put(KEY_NAME, songInfoModel.getSongName());
values.put(KEY_ARTIST, songInfoModel.getArtistName());
values.put(KEY_DURATION, songInfoModel.getDuration());
values.put(KEY_LAST_PLAYED, getDateTime());
values.put(KEY_ART, songInfoModel.getAlbumIDArtwork());
if(!CheckIsDataAlreadyInDBorNot(songInfoModel)) {
values.put(KEY_MOST_PLAYED, count);
Log.i("Doesn't Exists:", String.valueOf(count));
}
else {
values.put(KEY_MOST_PLAYED, count++);
Log.i("Exists:", String.valueOf(count));
}
db.insert(TABLE_NAME,null,values);
db.close();
Log.i("Time song was clicked:", getDateTime());
Code to check if song already exists:
public boolean CheckIsDataAlreadyInDBorNot(SongInfoModel songInfoModel) {
SQLiteDatabase db = this.getWritableDatabase();
String Query = "Select * from " + TABLE_NAME + " where " + KEY_ID + " = " + songInfoModel.getSongID();
Cursor cursor = db.rawQuery(Query, null);
if(cursor.getCount() <= 0){
cursor.close();
return false;
}
cursor.close();
return true;
}
Code to display the array of songs:
public ArrayList<SongInfoModel> getMostPlayed(){
ArrayList<SongInfoModel> mpList = new ArrayList<>();
String query = "SELECT * FROM " + TABLE_NAME + " ORDER BY " + KEY_MOST_PLAYED + " DESC";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query,null);
if(cursor.moveToFirst()){
do{
long id = cursor.getLong(0);
String SongName = cursor.getString(1);
String artistName = cursor.getString(2);
long dur = cursor.getLong(3);
String Art = cursor.getString(5);
SongInfoModel sh = new SongInfoModel(id,SongName,artistName,dur,null,Art);
mpList.add(sh);
}while (cursor.moveToNext());
}cursor.close();
return mpList;
Change to boolean CheckIsDataAlreadyInDBorNot to int CheckIsDataAlreadyInDBorNot,
public int CheckIsDataAlreadyInDBorNot(SongInfoModel songInfoModel) {
int mostPlayed=-1;
SQLiteDatabase db = this.getWritableDatabase();
String Query = "Select KEY_MOST_PLAYED from " + TABLE_NAME + " where " + KEY_ID + " = " + songInfoModel.getSongID();
Cursor cursor = db.rawQuery(Query, null);
if(cursor.getCount() <= 0){
cursor.close();
return -1;
}else {
if(cursor.moveToFirst()){
mostPlayed = cursor.getInt(....)
}
}
cursor.close();
return mostPlayed;
}
then use
count = CheckIsDataAlreadyInDBorNot(songInfoModel)+1;
//add count to SongInfoModel
values.put(KEY_MOST_PLAYED, count);
db.insert(TABLE_NAME,null,values);
db.close();
If I understand correctly, if the song doesn't exist you want to add it, if the song does exist then you want to increment the count of how many times the song has been played within the song.
In SQLite terms new song = INSERT, change song (count) = UPDATE.
You appear to only attempt to INSERT, which would either add another row for the song or not add one due to the KEY_ID already existing (or other constraints). Which, depends upon how the KEY_ID column has been defined. I suspect the latter which would explain the sort not working as expected due to the count never being increased.
Additionally count++ (post increment) increments the value AFTER the value has been applied. So that to would have resulted in the count not being incremented even though the message in the log would indicate that it has been incremented. ++count (pre increment) increments the value BEFORE it is applied, so this has been used.
As such I believe that you need something along the lines of :-
public void addSong(SongInfoModel songInfoModel){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
if(!CheckIsDataAlreadyInDBorNot(songInfoModel)) {
values.put(KEY_ID, songInfoModel.getSongID());
values.put(KEY_NAME, songInfoModel.getSongName());
values.put(KEY_ARTIST, songInfoModel.getArtistName());
values.put(KEY_DURATION, songInfoModel.getDuration());
values.put(KEY_LAST_PLAYED, getDateTime());
values.put(KEY_ART, songInfoModel.getAlbumIDArtwork());
values.put(KEY_MOST_PLAYED, count);
Log.i("Doesn't Exists:", String.valueOf(count));
db.insert(TABLE_NAME,null,values);
}
else {
values.put(KEY_MOST_PLAYED, ++count);
db.update(TABLE_NAME,values,
KEY_ID + "=?,
new String[]{songInfoModel.getSongID()}
);
Log.i("Exists:", String.valueOf(count));
}
db.close();
Log.i("Time song was clicked:", getDateTime());
}
Notes
- The code hasn't been checked so it may contain errors.
So I am trying to delete only one instance of values from my SQLite database using the where args of db.update and db.delete in my app but it seems to delete and update all the matching ones. I only want to update or delete one. How do I do it? I tried using cursors but my implementation seems wrong. Help?
public long updateData(String oldFood, String oldCalorie, String newFood, Float newCalorie, String newDate) {
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(helper.FOOD_COLUMN, newFood);
contentValues.put(helper.CALORIE_COLUMN, newCalorie);
contentValues.put(helper.DATE, newDate);
String[] columns = {helper.UID, helper.FOOD_COLUMN, helper.CALORIE_COLUMN, helper.DATE};
//long id = db.update(helper.TABLE_NAME, contentValues, helper.FOOD_COLUMN + " = ? AND " + helper.CALORIE_COLUMN + " = ?", new String[]{oldFood, oldCalorie});
Cursor cursor = db.query(helper.TABLE_NAME, columns, null, null, null, null, null);
Information current = new Information();
int uidIndex = cursor.getColumnIndex(helper.UID);
int cid = cursor.getInt(uidIndex);
int foodIndex = cursor.getColumnIndex(helper.FOOD_COLUMN);
int calorieIndex = cursor.getColumnIndex(helper.CALORIE_COLUMN);
float oldCalorieFloat = Float.parseFloat(oldCalorie);
if (oldFood == cursor.getString(foodIndex) && oldCalorieFloat == cursor.getFloat(calorieIndex)) {
long id = db.update(helper.TABLE_NAME, contentValues, helper.FOOD_COLUMN + " = ? AND " + helper.CALORIE_COLUMN + " = ?", new String[]{oldFood, oldCalorie});
db.close();
return id;
}
long id=0;
return id;
}
I want to create a android sqlite table with only one single row, for insert or update and read, how can i do that, cause i only know how to create mulitiple row
the table contain only 1 single row , is for user insert data and store , when read the data will come out , and last the update mean it will override the data which in that row.
this is my DBHelperNote.java
public static final String TABLE_GOAL = "goal";
public static final String GOAL_ID= "goal_id";
public static final String GENDER = "gender";
public static final String AGE = "age";
public static final String HEIGHT = "height";
public static final String WEIGHT = "weight";
public static final String GOAL = "goal";
private static final String CREATE_TABLE_GOAL = "CREATE TABLE " + TABLE_GOAL + " (" +
GOAL_ID + " INTEGER PRIMARY KEY, " +
GENDER + " text not null, " +
AGE + " text not null, "+
HEIGHT + " text not null, "+
WEIGHT + " text not null, "+
GOAL + " text not null "+
" );";
this is SQLControlerWeight.java
public void insertGoal(String gd,String age,String hg,String wg,,String gl) {
ContentValues cv = new ContentValues();
cv.put(DBHelperNote.GENDER, gd);
cv.put(DBHelperNote.AGE, age);
cv.put(DBHelperNote.HEIGHT, hg);
cv.put(DBHelperNote.WEIGHT, wg);
cv.put(DBHelperNote.GOAL, gl);
database.insert(DBHelperNote.TABLE_GOAL, null, cv);
}
public Cursor readGoal() {
String[] allColummn = new String[] {
DBHelperNote.GOAL_ID,
DBHelperNote.GENDER,
DBHelperNote.AGE,
DBHelperNote.HEIGHT,
DBHelperNote.WEIGHT,
DBHelperNote.GOAL,
};
Cursor c = database.query(DBHelperNote.TABLE_GOAL, allColummn, null,
null, null, null, null);
if (c != null) {
c.moveToFirst();
}
return c;
}
this actually is the method to read data put but it is from array mean multiple row , but i don't Know how to change it to only call one row
dbconnection = new SQLControlerWeight(this);
dbconnection.openDatabase();
Cursor cursor = dbconnection.readGoal();
String[] from = new String[]{
DBHelperNote.GOAL_ID,
DBHelperNote.GENDER,
DBHelperNote.AGE,
DBHelperNote.HEIGHT,
DBHelperNote.WEIGHT,
DBHelperNote.GOAL,
};
int[] to = new int[]{
R.id.goal_id,
R.id.field_gender,
R.id.field_age,
R.id.field_height,
R.id.field_weight,
R.id.field_goal,
};
As you will be having GOAL_ID ones inserted you can modify you insert function as
public void insertGoal(String goal_id, String gd,String age,String hg,String wg,,String gl) {
ContentValues cv = new ContentValues();
if (goal_id != null){
cv.put(DBHelperNote.GOAL_ID, goal_id);
}
cv.put(DBHelperNote.GENDER, gd);
cv.put(DBHelperNote.AGE, age);
cv.put(DBHelperNote.HEIGHT, hg);
cv.put(DBHelperNote.WEIGHT, wg);
cv.put(DBHelperNote.GOAL, gl);
database.replace(DBHelperNote.TABLE_GOAL, null, cv);
}
Modification done is changed insert to replace, which will make sure if primary key value is provided and row exists with that id then it will replace the existing row without creating new one. Most important is the if condition for checking whether goal_id is null or not, if null then don't provide that in contentvalue.
Modify the if condition properly for proper comparison for Goal_id as i have just used the one i can visualize from your question content.
Use RawQuery method its little bit easier.
c1 = db.rawQuery("select * from Table_Name", null);
c1.moveToFirst();
do {
str = c1.getString(c1.getColumnIndex("FieldName"));
ab.add(str);
} while (c1.moveToNext());
I'm trying to copy all the data in a database table, which correspond to the WHERE clause, and insert them into another table. I'm trying this code, but in the table prev there are only 2 records in the table Ver are inserted more than 100 records .... why?
private void Tras() {
String numero_ricevuto = (i.getStringExtra("numero"));
SQLiteDatabase db = mHelper.getWritableDatabase();
String sql = "SELECT data, unita_di_misura FROM prev WHERE numero ='"+numero_ricevuto+"'";
Cursor c = db.rawQuery(sql, null);
int count = c.getCount();
String[] data = new String[count];
String[] unita_di_misura = new String[count];
for(int i=0; i<count; i++) {
c.moveToNext();
data[i] = c.getString(0);
unita_di_misura[i] = c.getString(1);
}
for(int i=0 ;i < data.length;i++){
ContentValues cv = new ContentValues();
cv.put(VerTable.SI_NO, "0");
cv.put(VerTable .DATA, data[i]);
cv.put(VerTable .U_M, e.unita_di_misura[i]);
db.insert(VerTable .TABLE_NAME, null, cv);
}
c.close();
db.close();
}
Try this 3 line solution hope this will help you
private void Tras() {
String numero_ricevuto = (i.getStringExtra("numero"));
SQLiteDatabase db = mHelper.getWritableDatabase();
String sql = "INSERT INTO "+VerTable .TABLE_NAME+" SELECT 0,data, unita_di_misura FROM prev WHERE numero = '"+numero_ricevuto+"'";
db.execSQL(sql);
db.close();
}
but in the table prev there are only 2 records in the table Ver are inserted more than 100 records .... why?
Possibly you ran the code more than once.
Also, pulling data from db only to insert it back is not very efficient. It's better to let the database engine do the work for you, e.g.
db.execSQL("INSERT INTO " + VerTable.TABLE_NAME +
"(" + VerTable.SI_NO + "," VerTable.DATA + "," + VerTable.U_M + ") " +
"SELECT 0, data, unita_di_misura FROM prev WHERE numero=?",
new String[] { numero_ricevuto });
Using ? params also avoids the possiblity of string SQL injection.
I'm attempting to do the following SQL query within Android:
String names = "'name1', 'name2"; // in the code this is dynamically generated
String query = "SELECT * FROM table WHERE name IN (?)";
Cursor cursor = mDb.rawQuery(query, new String[]{names});
However, Android does not replace the question mark with the correct values. I could do the following, however, this does not protect against SQL injection:
String query = "SELECT * FROM table WHERE name IN (" + names + ")";
Cursor cursor = mDb.rawQuery(query, null);
How can I get around this issue and be able to use the IN clause?
A string of the form "?, ?, ..., ?" can be a dynamically created string and safely put into the original SQL query (because it is a restricted form that does not contain external data) and then the placeholders can be used as normal.
Consider a function String makePlaceholders(int len) which returns len question-marks separated with commas, then:
String[] names = { "name1", "name2" }; // do whatever is needed first
String query = "SELECT * FROM table"
+ " WHERE name IN (" + makePlaceholders(names.length) + ")";
Cursor cursor = mDb.rawQuery(query, names);
Just make sure to pass exactly as many values as places. The default maximum limit of host parameters in SQLite is 999 - at least in a normal build, not sure about Android :)
Here is one implementation:
String makePlaceholders(int len) {
if (len < 1) {
// It will lead to an invalid query anyway ..
throw new RuntimeException("No placeholders");
} else {
StringBuilder sb = new StringBuilder(len * 2 - 1);
sb.append("?");
for (int i = 1; i < len; i++) {
sb.append(",?");
}
return sb.toString();
}
}
Short example, based on answer of user166390:
public Cursor selectRowsByCodes(String[] codes) {
try {
SQLiteDatabase db = getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String[] sqlSelect = {COLUMN_NAME_ID, COLUMN_NAME_CODE, COLUMN_NAME_NAME, COLUMN_NAME_PURPOSE, COLUMN_NAME_STATUS};
String sqlTables = "Enumbers";
qb.setTables(sqlTables);
Cursor c = qb.query(db, sqlSelect, COLUMN_NAME_CODE+" IN (" +
TextUtils.join(",", Collections.nCopies(codes.length, "?")) +
")", codes,
null, null, null);
c.moveToFirst();
return c;
} catch (Exception e) {
Log.e(this.getClass().getCanonicalName(), e.getMessage() + e.getStackTrace().toString());
}
return null;
}
Sadly there's no way of doing that (obviously 'name1', 'name2' is not a single value and can therefore not be used in a prepared statement).
So you will have to lower your sights (e.g. by creating very specific, not reusable queries like WHERE name IN (?, ?, ?)) or not using stored procedures and try to prevent SQL injections with some other techniques...
As suggest in accepted answer but without using custom function to generate comma-separated '?'. Please check code below.
String[] names = { "name1", "name2" }; // do whatever is needed first
String query = "SELECT * FROM table"
+ " WHERE name IN (" + TextUtils.join(",", Collections.nCopies(names.length, "?")) + ")";
Cursor cursor = mDb.rawQuery(query, names);
You can use TextUtils.join(",", parameters) to take advantage of sqlite binding parameters, where parameters is a list with "?" placeholders and the result string is something like "?,?,..,?".
Here is a little example:
Set<Integer> positionsSet = membersListCursorAdapter.getCurrentCheckedPosition();
List<String> ids = new ArrayList<>();
List<String> parameters = new ArrayList<>();
for (Integer position : positionsSet) {
ids.add(String.valueOf(membersListCursorAdapter.getItemId(position)));
parameters.add("?");
}
getActivity().getContentResolver().delete(
SharedUserTable.CONTENT_URI,
SharedUserTable._ID + " in (" + TextUtils.join(",", parameters) + ")",
ids.toArray(new String[ids.size()])
);
Actually you could use android's native way of querying instead of rawQuery:
public int updateContactsByServerIds(ArrayList<Integer> serverIds, final long groupId) {
final int serverIdsCount = serverIds.size()-1; // 0 for one and only id, -1 if empty list
final StringBuilder ids = new StringBuilder("");
if (serverIdsCount>0) // ambiguous "if" but -1 leads to endless cycle
for (int i = 0; i < serverIdsCount; i++)
ids.append(String.valueOf(serverIds.get(i))).append(",");
// add last (or one and only) id without comma
ids.append(String.valueOf(serverIds.get(serverIdsCount))); //-1 throws exception
// remove last comma
Log.i(this,"whereIdsList: "+ids);
final String whereClause = Tables.Contacts.USER_ID + " IN ("+ids+")";
final ContentValues args = new ContentValues();
args.put(Tables.Contacts.GROUP_ID, groupId);
int numberOfRowsAffected = 0;
SQLiteDatabase db = dbAdapter.getWritableDatabase());
try {
numberOfRowsAffected = db.update(Tables.Contacts.TABLE_NAME, args, whereClause, null);
} catch (Exception e) {
e.printStackTrace();
}
dbAdapter.closeWritableDB();
Log.d(TAG, "updateContactsByServerIds() numberOfRowsAffected: " + numberOfRowsAffected);
return numberOfRowsAffected;
}
This is not Valid
String subQuery = "SELECT _id FROM tnl_partofspeech where part_of_speech = 'noun'";
Cursor cursor = SQLDataBase.rawQuery(
"SELECT * FROM table_main where part_of_speech_id IN (" +
"?" +
")",
new String[]{subQuery}););
This is Valid
String subQuery = "SELECT _id FROM tbl_partofspeech where part_of_speech = 'noun'";
Cursor cursor = SQLDataBase.rawQuery(
"SELECT * FROM table_main where part_of_speech_id IN (" +
subQuery +
")",
null);
Using ContentResolver
String subQuery = "SELECT _id FROM tbl_partofspeech where part_of_speech = 'noun' ";
final String[] selectionArgs = new String[]{"1","2"};
final String selection = "_id IN ( ?,? )) AND part_of_speech_id IN (( " + subQuery + ") ";
SQLiteDatabase SQLDataBase = DataBaseManage.getReadableDatabase(this);
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables("tableName");
Cursor cursor = queryBuilder.query(SQLDataBase, null, selection, selectionArgs, null,
null, null);
In Kotlin you can use joinToString
val query = "SELECT * FROM table WHERE name IN (${names.joinToString(separator = ",") { "?" }})"
val cursor = mDb.rawQuery(query, names.toTypedArray())
I use the Stream API for this:
final String[] args = Stream.of("some","data","for","args").toArray(String[]::new);
final String placeholders = Stream.generate(() -> "?").limit(args.length).collect(Collectors.joining(","));
final String selection = String.format("SELECT * FROM table WHERE name IN(%s)", placeholders);
db.rawQuery(selection, args);