Related
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.
here is my code for using that string
String item = item1.getText().toString();
item = item.toLowerCase();
String date = getDate();
Datahelper edited = new Datahelper(this);
edited.open();
String returnedprice = edited.getprice(item,date);
String returneddetail = edited.getdetail(item,date);
edited.close();
price.setText(returnedprice);
details.setText(returneddetail);
and this is my method in sqlite
public String getprice(String item ,String date) {
// TODO Auto-generated method stub
String[] columns = new String[]{KEY_ROWID, KEY_CATEGORY,KEY_DATE,KEY_PRICE,KEY_DETAILS};
String whereClause = KEY_CATEGORY + " = ? and " + KEY_DATE + " = ?";
String[] whereArgs = {item,date};
Cursor c = ourDatabase.query("DATABASE_TABLE", columns, whereClause, whereArgs, null, null, null);
if(c!=null){
String price = c.getString( c.getColumnIndex(KEY_PRICE));
return price;
}
return null;
}
public String getdetail(String item, String date) {
// TODO Auto-generated method stub
String[] columns = new String[]{KEY_ROWID, KEY_CATEGORY,KEY_DATE,KEY_PRICE,KEY_DETAILS};
String whereClause = KEY_CATEGORY + " = ? and " + KEY_DATE + " = ?";
String[] whereArgs = {item, date};
Cursor c = ourDatabase.query("DATABASE_TABLE", columns, whereClause, whereArgs, null, null, null);
if(c!=null){
String detail = c.getString( c.getColumnIndex(KEY_DETAILS));
return detail;
}
return null;
}
my app gets crash when using this code,, i dont know waht's wrong in the code if anyone need i can post the whole code pls help me
the column index has nothing in common with the returned set:
String price = c.getString( c.getColumnIndex(KEY_PRICE));
in c you have only 5 columns (0 to 4) ( String[] columns = new String[]{KEY_ROWID, KEY_CATEGORY,KEY_DATE,KEY_PRICE,KEY_DETAILS}; ) and c.getColumnIndex(KEY_PRICE) return the actual index in the table which is probably higher than 4.
instead of
String price = c.getString( c.getColumnIndex(KEY_PRICE));
try
String price = c.getString(3);
the corect code is -
public String getprice(String item ,String date) {
// TODO Auto-generated method stub
String[] columns = new String[]{KEY_ROWID, KEY_CATEGORY,KEY_DATE,KEY_PRICE,KEY_DETAILS};
String whereClause = KEY_CATEGORY + " = ? and " + KEY_DATE + " = ?";
String[] whereArgs = {item,date};
Cursor c = ourDatabase.query("DATABASE_TABLE", columns, whereClause, whereArgs, null, null, null);
if(c!=null){
c.moveToFirst();
String price = c.getString( c.getColumnIndex(KEY_PRICE));
return price;
}
return null;
}
public String getdetail(String item, String date) {
// TODO Auto-generated method stub
String[] columns = new String[]{KEY_ROWID, KEY_CATEGORY,KEY_DATE,KEY_PRICE,KEY_DETAILS};
String whereClause = KEY_CATEGORY + " = ? and " + KEY_DATE + " = ?";
String[] whereArgs = {item, date};
Cursor c = ourDatabase.query("DATABASE_TABLE", columns, whereClause, whereArgs, null, null, null);
if(c!=null){
c.moveToFirst();
String detail = c.getString( c.getColumnIndex(KEY_DETAILS));
return detail;
}
return null;
}
I am trying, unsucessfully, to query my database to find the maximum 'area number', in my areas table for a certain inspection, so that I can set the text in a form to the next area number.
The database table consists of four columns; _id, inpsection_link, area_number, area-reference.
I have created the following in my database helper class (using this post as a guide: SQLiteDatabase.query method):
public int selectMaxAreaNumber (long inspectionId) {
String inspectionIdString = String.valueOf(inspectionId);
String[] tableColumns = new String[] {
AREA_NUMBER,
"(SELECT max(" + AREA_NUMBER + ") FROM " + AREAS_TABLE + ") AS max"
};
String whereClause = INSPECTION_LINK + " = ?";
String[] whereArgs = new String[] {
inspectionIdString
};
Cursor c = rmDb.query(AREAS_TABLE, tableColumns, whereClause, whereArgs,
null, null, null);
int maxAreaNumber = c.getColumnIndex("max");
return maxAreaNumber;
}
Which I then call in the areaEdit class as follows:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rmDbHelper = new RMDbAdapter(this);
rmDbHelper.open();
Intent i = getIntent();
inspectionId = i.getLongExtra("Intent_InspectionID", -1);
areaId = i.getLongExtra("Intent_AreaID", -1);
if (areaId == -1) {
nextAreaNumber = rmDbHelper.selectMaxAreaNumber(inspectionId) + 1;
Toast.makeText(getApplicationContext(), String.valueOf(nextAreaNumber),
Toast.LENGTH_LONG).show();
}
setContentView(R.layout.edit_area);
setUpViews();
populateFields();
setTextChangedListeners();
}
However, it just returns 1 everytime (even if there are numbers higher than that stored in the database).
Confused.com!! Any help much appreciated.
Your issue is here :
int maxAreaNumber = c.getColumnIndex("max");
You're getting the column index of max, which is 1 because you only have one column in your query.
Instead, do something like this :
int maxAreaNumber = 0;
if(c.moveToFirst())
{
maxAreaNumber = c.getInt(1);
// or cleaner
maxAreaNumber = c.getInt(c.getColumnIndex("max"));
}
else
// no data in cursor
protected void cal() {
// TODO Auto-generated method stub
Company obj1 = new Company();
String val1 = obj1.getCompanyID();
edite.getText().toString();
Cursor sumw = dbObject.rawQuery(
"SELECT sum(Volume)FROM SHARE WHERE _id2 LIKE ?",
new String[] { "%" + val1 + "%" });
sumw.moveToFirst();
editT.setText(sumw.getString(0));
}
i want to get the value of the EditText value, to multiply with the selected sum value of the query
Try this,
protected void cal() {
// TODO Auto-generated method stub
Company obj1 = new Company();
String val1 = obj1.getCompanyID();
int n = Integer.parseInt(edite.getText().toString());
Cursor sumw = dbObject.rawQuery(
"SELECT sum(Volume)FROM SHARE WHERE _id2 LIKE ?",
new String[] { "%" + val1 + "%" });
sumw.moveToFirst();
editT.setText(sumw.getString(0)*n);
}
If they're integer values, try this:
int multiplierOne = Integer.parseInt(edite.getText().toString());
....
int multiplierTwo = sumw.getInt(0);
int product = multiplierOne * multiplierTwo;
editT.setText("" + product);
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);