Why RawQuery in SQLite works only without Question Mark (Kotlin) - android

This is the structure and data of my table funco inside database your.db (SQLite):
Id Disp Tp
1 Ball 2
2 Light 1
3 Sea 4
This is my code:
var db = SQLiteDatabase.openDatabase(this.filesDir.path +
"/your.db",null, SQLiteDatabase.OPEN_READWRITE)
val c = db.rawQuery("Select Id, Disp, Tp From Funco Where Id<=2;",null)
var stat = c.moveToFirst()
var result=""
while (stat) {
val mId = c.getInt(c.getColumnIndex("Id"))
val mDisp = c.getString(c.getColumnIndex("Disp"))
val mTp = c.getInt(c.getColumnIndex("Tp"))
result += "$mId $mDisp $mTp | "
stat = c.moveToNext()
}
c.close()
db.close()
The result value:
1 Ball 2 | 2 Light 1 |
If I replace my second line for
val c = db.rawQuery("Select ?, ?, ? From Funco Where ?<=2;"
,arrayOf("Id","Disp","Tp","Id"))
There is no error raised, but the cursor is empty!
Why?
Update:
The #tynn answer is right. I think that the documentation is subtle.
I think that compiler shoud be throw an error and not just return empty cursor.
In the same flavor, one can write
val c = db.query("funco",arrayOf("Id","Disp","Tp"),"Id<=?",
arrayOf("2"),null,null,null)
But below code fails
val c = db.query("funco",arrayOf("Id","Disp","Tp"),"?<=?",
arrayOf("id","2"),null,null,null)

As the documentation states:
selectionArgs String: You may include ?s in where clause in the query, which will be replaced by the values from selectionArgs. The values will be bound as Strings.
The question marks are there to bind different values to a precompiled SQL query. This functionality is not suitable for configuring the columns to be used in the query. Your first approach is the correct one. You could make the 2 in it dynamic though:
db.rawQuery("Select Id, Disp, Tp From Funco Where Id<=?;", arrayOf("2"))

Related

Query that brings all the parents whose children meet a certain criteria

I need a query that brings all the parents whose children meet a certain criteria.
So far this is giving me something different than expected:
#Transaction
#Query("SELECT * FROM piece_unit_table put WHERE (SELECT et.date_of_creation FROM expense_table et WHERE et.piece_unit_parent_id = put.piece_unit_id) = :dateOfExpenseCreation AND project_id = :projectId")
public abstract LiveData<List<PieceUnit>> getPieceUnitsWhereDateOfExpenseCreation(long dateOfExpenseCreation, int projectId);
A more readable version of the same code above:
SELECT *
FROM piece_unit_table put
WHERE (
SELECT et.date_of_creation
FROM expense_table et
WHERE et.piece_unit_parent_id = put.piece_unit_id
) = :dateOfExpenseCreation
AND project_id = :projectId
At first I thought I got a jackpot with that one because it gave me responses on the first try (I usually spend a whole day just thinking about the structure of the query, I hate them), I never gave it too much thought, but the days passed by and it stopped giving me responses, so I'm guessing that maybe, the query was comparing some other dates inside the WHERE clause, more precisely, the dates on which the pieces where created.
Now that I reread it again it looks like a pretty bad query...
The way in which I'm storing dates (from the children side):
public void insertExpense(Expense expense) {
long now = Converters.dateToTimestamp(LocalDate.now());
expense .setDate_of_creation(now);
long edited = System.currentTimeMillis();
expense .setLast_edited(edited);
Log.println(Log.WARN, TAG, "insertExpense: date of creation is: " + expense.getDate_of_creation());
Log.println(Log.WARN, TAG, "insertExpense: expense project is: " + expense.getParent_project_id());
Log.println(Log.WARN, TAG, "insertExpense: expense piece parent is: " + expense.getPiece_unit_parent_id());
insert(expense);
}
The observer side:
Log.println(Log.ERROR, TAG, "getPieceUnitGroupedByExpenseProductsAtDateAndProject: specific date is: " + specificDate);
Log.d(TAG, "getPieceUnitGroupedByExpenseProductsAtDateAndProject: project id is: " + project);
MyObserver
.observeOnce(
pieceUnitRepository.getPieceUnitsWhereDateOfExpenseCreation(
specificDate,
project
),
pieceUnits ->
{
Log.d(TAG, "getPieceUnitGroupedByExpenseProductsAtDateAndProject: piece units are: " + pieceUnits);
if (pieceUnits != null && pieceUnits.size() > 0) {
PieceUnit p = pieceUnits.get(0);
Log.println(Log.WARN, TAG, "getPieceUnitGroupedByExpenseProductsAtDateAndProject: Hello there ;) : " + p.getBeginning_date());
}
The Logd's:
insertExpense: date of creation is: 18469
insertExpense: expense project is: 1
insertExpense: expense piece parent is: 4
getPieceUnitGroupedByExpenseProductsAtDateAndProject: specific date is: 18469
getPieceUnitGroupedByExpenseProductsAtDateAndProject: project id is: 1
getPieceUnitGroupedByExpenseProductsAtDateAndProject: piece units are: []
I'm not asking for a full solution, but any input could point me in the right direction, so thanks in advance.
Final answer (from second update):
SELECT
put.*
FROM piece_unit_table put
WHERE
EXISTS(
SELECT
1
FROM
expense_table et
WHERE
et.date_of_creation = :dateOfExpenseCreation AND et.piece_unit_parent_id = piece_unit_id
) AND
put.project_id = :projectId
EDITS
First answer :
In SQL there seems to be a function that helps on these cases:
all or not exist
https://stackoverflow.com/a/42439405/11214643
In the SQLite case there doesn't seem to be a direct equivalence, and every solution seems to be a case by case workaround depending on the type of clause.
In my case I LEFT JOIN-ed the children table, but group by-ing it before the join to avoid repeated rows on the left table.
As is expected, it was during the child table sub-query, that I filtered the table by their date condition
SELECT put.*
FROM piece_unit_table put
LEFT JOIN(
SELECT
piece_unit_parent_id
FROM
expense_table et
WHERE
et.date_of_creation = :dateOfExpenseCreation
GROUP BY
et.piece_unit_parent_id
) et1
ON
et1.piece_unit_parent_id = put.piece_unit_id
WHERE
put.project_id = :projectId
UPDATE:
It seems that behind curtains(either be SQLite or the ROOM interface), the LEFT JOIN is happening before the sub-query executes its own WHERE clause, as a result, the query is giving me parents, even when there are no children that meet the criteria, so the solution was to bring an additional column from the child temp table to the 'front', and check for it's non null-ability.
SELECT
put.*,
et1.puId AS expPuId
FROM piece_unit_table put
LEFT JOIN(
SELECT
piece_unit_parent_id AS puId
FROM
expense_table et
WHERE
et.date_of_creation = :dateOfExpenseCreation
GROUP BY
et.piece_unit_parent_id
) et1
ON
et1.puId = put.piece_unit_id
WHERE
put.project_id = :projectId AND expPuId NOT NULL
UPDATE 2:
Thanks to #forpas
The EXISTS and NOT EXISTS operators are in fact supported by SQLite, also, what was bringing me parents even when no children met the criteria was the second WHERE clause that was applied directly to the left table, but even if there was no clause applied to this table, it would've still gave me answers, because that's how LEFT JOIN works, if there are no matches it fills them with NULL's.
Here is a query that has the expected result, but does it better.
SELECT
put.*
FROM piece_unit_table put
WHERE
EXISTS(
SELECT
1
FROM
expense_table et
WHERE
et.date_of_creation = :dateOfExpenseCreation AND et.piece_unit_parent_id = piece_unit_id
) AND
put.project_id = :projectId

Sqlite Android order by & group_concat

I'm using the sqlite3 : 3.8.2 with sqliteman (Ububuntu 14.04) to try the query that I need in my program Android.
Android version 4.0+.
I have 3 table to use to obtain data
I found this problem:
when I use this query in sqliteman
SELECT st.nome
AS nome,
st.cognome
AS cognome,
(SELECT ae.usercode
AS usercode
FROM assenze ae
WHERE st.ultimo_evento = ae.fk_evento)
AS ultimo_evento,
(SELECT
GROUP_CONCAT(
ev.nome_evento, ", "
)
AS nome_evento
FROM eventi ev
WHERE ev.studente_id = st.id
)
AS nome_evento
FROM studenti st
WHERE st.classe_ident ='4297'
ORDER BY st.cognome
the output is:
EP, USC, R1, R, A ... perfect for me
but when I use it in android with db.rawQuery() the result is
A, EP, R, R1, USC ... no good for me
also seems to be the same result of this query (execute in sqliteman and android):
SELECT
st.nome AS nome,
st.cognome AS cognome,
ae.usercode AS ultimo_evento,
GROUP_CONCAT(
ev.nome_evento, ", "
)
AS nome_evento
FROM studenti st
LEFT JOIN eventi ev
ON st.id = ev.studente_id
LEFT JOIN assenze ae
ON st.ultimo_evento = ae.fk_evento
WHERE st.classe_ident ='4297'
GROUP BY st.id
ORDER BY st.cognome
Probably it's my fault but I'm still trying to find a solution...
Thanks
seems that it works also without ORDER BY ev.data_ora_evento. But in android still not working...
The group_concat() documentation says:
The order of the concatenated elements is arbitrary.
However, you could try to force an order by executing group_concat() on a subquery that is sorted:
SELECT GROUP_CONCAT(ev.nome_evento, ", ") AS nome_evento
FROM (SELECT nome_evento
FROM eventi
WHERE studente_id = st.id
ORDER BY whatever) AS ev

How to make... SQLite select DISTINCT -> query(true, ...)

My query is:
String _query = "select DISTINCT Y.CATE_CODE4 CATE_CODE, Y.CATE_NAME4 CATE_NAME,
0 IS_PRESET from G_MASTER X INNER JOIN CATEGORY_VIEW Y ON X.CATE_4 = Y.CATE_CODE4
where X.IS_SALE = 1 AND Y.IS_LOCKED4 = 0 AND Y.CATE_NAME2 not in ('XXXX')";
How does Android SQLite make db.query()?
First of all you should read SQLiteDatabase reference.
You could use a rawQuery(), here's an example of it :
rawQuery - db.rawQuery("select DISTINCT Y.CATE_CODE4 CATE_CODE, Y.CATE_NAME4 CATE_NAME,
0 IS_PRESETG_MASTER X INNER JOIN CATEGORY_VIEW Y ON X.CATE_4 = Y.CATE_CODE4 from table where X.IS_SALE = 1 AND Y.IS_LOCKED4 = 0 AND Y.CATE_NAME2 not in ('XXXX')"",new String[]{"data"});
It's just an example, you have to adapt your code to this.

Android sqlite db query can't get correct data

I have a question.
Have a table like this:
(table name: times)
+-------+-----------+---------+
| block | startTime | endTime |
+-------+-----------+---------+
| 1 | 08:00 | 10:00 |
+-------+-----------+---------+
when I use rawquery like this:
String ctime = "'09:45'";
cursor = sdb.rawQuery(String.format("select block from times where startTime < time(%s) and endTime> time(%s)", ctime,ctime), null);
I Can get the correct data "1".
But when I use query like this:
cursor = sdb.query("times", new String[]{"block"}, "startTime <= time(?) and EndTime >= time(?)", new String[]{ctime,ctime}, null, null, null);
I can't get the correct data.
The Cursor count is 0.
Why??
A valid time string must not contain quotes.
The quotes in your ctime variable are correct when you are constructing the SQL statement by hand, but they are used to delimit the SQL string and are not part of the value of the SQL string.
When you are using parameters, you do not need to put quotes around strings (unless you actually want them to be part of the string's value).

Display all records that are stored in SQlite database

I want to display all my records that are stored in the database
c = db.DBhelper.getChamp1(c.getCount);
//startManagingCursor(c);
int j = 0;
stateNameArray = new String[c.getCount()];
c.moveToFirst();
while(!c.isAfterLast()) {
stateNameArray[j] = c.getString(0);
j++;
Log.i("DEBUG_SQL","" + c.getString(0)+ " "+c.getString(j));
c.moveToNext();
}
//String resultat = ;
Log.i("DEBUG_SQL","" + c.getColumnIndex("article"));
I get an error when I write c.getCount – why? When I write a number like 1 or 2 or 3... that works.
And if I write
c = db.rawQuery("SELECT * FROM loan", null);
I get an error, but if I write
db.rawQuery("SELECT * FROM loan WHERE _id=1", null);
That works. Why?
At the very least, I see a problem with this log statement, where c.getString(j) doesn't make sense. And it may trigger an error as j gets larger.
Log.i("DEBUG_SQL","" + c.getString(0)+ " "+c.getString(j));
What data did you intend to access with the statement c.getString(j)?
On the getCount error. I assumed the error in the following was a typo. But is this where the error associated with getCount was located?
c = db.DBhelper.getChamp1(c.getCount);
But I shouldn't assume - you never know. It should read (add brackets to the method call).
c = db.DBhelper.getChamp1(c.getCount());
And as #Barak mentioned, what is going on with this statement?
To answer the question about your getCount isuue, you get the error because of this:
c = db.DBhelper.getChamp1(c.getCount);
You're trying to get the count of the cursor before you have it (and you're missing ()).
This would work since you have a cursor to count before you pull the next one:
c = db.getSomeCursor;
c1 = db.DBhelper.getChamp1(c.getCount());
Let us know what you're trying to achieve (I can't figure it out from the code you posted) and maybe we can be more helpful.

Categories

Resources