This is sort of a continuation from my previous one, but I finally figured that one out (got rid of the duplication issue).
Android Room Relationship duplicating information
Customer table
#Entity(tableName = "customer_table")
public class Customer {
#ColumnInfo(name = "Customer_Serial", index = true)
#PrimaryKey
private int customerSerial;
#ColumnInfo(name = "Customer_Sort", index = true)
private String customerSort;
#ColumnInfo(name = "Customer_Name")
private String customerName;
public Customer(int customerSerial, String customerName) {
this.customerSerial = customerSerial;
this.customerName = customerName;
this.customerSort = String.format(Locale.ENGLISH, "%d-%d", new Date().getTime(), customerSerial);
}
}
Invoice table
#Entity(tableName = "invoice_table")
public class Invoice {
#ColumnInfo(name = "Invoice_Number", index = true)
#PrimaryKey
private int invoiceNumber;
#ColumnInfo(name = "Customer_Serial")
private int customerSerial;
#ColumnInfo(name = "Invoice_Sort", index = true)
private String invoiceSort;
#ColumnInfo(name = "Delivery_Status")
private int deliveryStatus;
public Invoice(int invoiceNumber, int customerSerial) {
this.invoiceNumber = invoiceNumber;
this.customerSerial = customerSerial;
this.invoiceSort = String.format(Locale.ENGLISH, "%d-%d", new Date().getTime(), invoiceNumber)
}
public void setDeliveryStatus(int deliveryStatus) {
this.deliveryStatus = deliveryStatus;
}
public int getDeliveryStatus() { return deliveryStatus; }
}
CustomerInvoice relation
public class CustomerInvoice {
#Embedded public Customer customer;
#Relation(
parentColumn = "Customer_Serial",
entityColumn = "Customer_Serial"
entity = Invoice.class
)
public List<Invoice> invoices;
}
DAO
public abstract class InvoiceDao {
#Transaction
#Query("SELECT * FROM invoice_table " +
"JOIN customer_table " +
"ON invoice_table.Debtor_Ser_No = customer_table.Customer_Serial " +
"WHERE invoice_table.Delivery_Status = :deliveryStatus " +
"GROUP BY customer_table.Customer_Serial " +
"ORDER BY customer_table.Customer_Sort, invoice_table.Invoice_Sort")
abstract public LiveData<List<CustomerInvoices>> getCustomerInvoices(int deliveryStatus);
abstract public void insert(Invoice... invoice);
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract public void insertCustomer(Customer... customer);
}
ViewModel
public LiveData<List> getCustomerInvoices(int deliveryStatus) { return dao.getCustomerInvoices(); }
Test
Invoice invoice1 = new Invoice(1234, 1);
Invoice invoice2 = new Invoice(1235, 1);
Invoice invoice3 = new Invoice(2468, 2);
Invoice invoice4 = new Invoice(2469, 2);
Customer customer1 = new Customer(1, "Customer 1");
Customer customer2 = new Customer(2, "Customer 2");
dao.insertCustomer(customer1);
dao.insertCustomer(customer2);
dao.insert(invoice1);
dao.insert(invoice2);
dao.insert(invoice3);
dao.insert(invoice4);
invoice1.setDeliveryStatus(0);
invoice2.setDeliveryStatus(0);
invoice3.setDeliveryStatus(0);
invoice4.setDeliveryStatus(0);
viewModel.getCustomerInvoices2(0).observe(getViewLifeCycleOwner(), list -> { ... });
If I debug the output of the observer, it returns correctly, 2 customers with 2 invoices each.
However, if I do
Test2
invoice1.setDeliveryStatus(1);
viewModel.getCustomerInvoices2(1).observe(getViewLifeCycleOwner(), list -> { ... });
It returns 1 customer with 2 invoices, instead of 1 customer with 1 invoice, as the 2nd invoice for that customer still has a delivery status of 0.
I realise the problem is in the CustomerInvoice relation where it is ignoring the where clause for the invoice_table itself (It still does the customer where clause perfectly).
However I just can't seem to wrap my head around to fix it.
I have Google searched for quite a while now, and I know it is because it is basically just doing 'Get customer where they have at least 1 invoice with the correct delivery status', then it is doing 'Get all invoices for this customer', just that pretty much everything I can find gives basic samples that don't involve LiveData at all, and I need it to be using LiveData.
One of the many attempts I tried to make it work, was to do a lot of the legwork in the viewmodel itself.
DAO
#Query("SELECT * FROM customer_table " +
"JOIN invoice_table " +
"ON customer_table.Customer_Serial = invoice_table.Debtor_Ser_No " +
"WHERE invoice_table.Delivery_Status = :deliveryStatus " +
"GROUP BY customer_table.Customer_Serial ORDER BY customer_table.Customer_Sort")
abstract public Maybe<List<Customer>> getCustomersByDeliveryStatus(int deliveryStatus);
#Query("SELECT * FROM invoice_table " +
"WHERE invoice_table.Debtor_Ser_No = :debtorSerial " +
"AND invoice_table.Delivery_Status = :deliveryStatus " +
"ORDER BY invoice_table.Invoice_Sort")
abstract public Single<List<Invoice>> getCustomerInvoicesByDeliveryStatus(int debtorSerial, int deliveryStatus);
ViewModel
public LiveData<List<Map<Customer, List<Invoice>>>> getCustomerInvoices2(int deliveryStatus) {
MutableLiveData<List<Map<Customer, List<Invoice>>>> liveCustomerInvoices = new MutableLiveData<>();
List<Map<Customer, List<Invoice>>> listCustomerInvoices = new ArrayList<>();
mInvoiceDao
.getCustomersByDeliveryStatus(deliveryStatus)
.subscribeOn(Schedulers.io())
.subscribe(
(customers) -> {
for (Customer customer : customers) {
mInvoiceDao.getCustomerInvoicesByDeliveryStatus(
customer.getCustomerSerial(),
deliveryStatus
).subscribeOn(Schedulers.io())
.subscribe(
(invoices) -> {
listCustomerInvoices.add(Collections.singletonMap(customer, invoices));
}
);
}
liveCustomerInvoices.postValue(listCustomerInvoices);
}, throwable -> Log.e("Error", "Error")
);
return liveCustomerInvoices;
}
While it does work (to a varying degree, the LiveData isn't updated instantly, so sometimes it shows nothing or sometimes it shows 1 thing only until I refresh the display), and my recyclerview shows exactly what I need it to show, it doesn't maintain the order based on 'Customer_Sort' and 'Invoice_Sort' which has to be maintained.
I understand why on that too, it's because 'map' doesn't guarantee order.
First issue I believe is that when you have #Embedded and then #Relation the #Embedded is considered the parents (Customers). That is Room basically ignores (at first) the children (Invoices).
You appear to be considering this from an Invoice perspective when Room considers it, as instructed by the #Embedded/#Relation, from the Customer perspective.
Once Room has (in theory) obtained the parents (customers) it then considers this from the object perspective and obtains ALL children (invoices), irrespective of SQL (e.g. WHERE ORDER) that affects the children retrieved and builds complete objects (all children for the parent).
The WHERE and ORDER are only affectual if it changes the number of parents.
This basically a convenience approach.
To affect the children (Invoices), prune them, sort them if using the Customer(#Embedded)Invoice(#Realtion) POJO needs a means of overriding Rooms handling.
Another issue is that your testing code changes the Invoice objects (e.g. invoice1.setDeliveryStatus(0);) but does not apply that change to the database. So if you extract from the database then those changes will not have been applied.
Without changing the CustomerInvoice class. Consider the following:-
Getters and setters added to the Customer and Invoice classes.
InvoiceDao transformed to be :-
#Dao
public abstract class InvoiceDao {
/*
#Transaction
#Query("SELECT * FROM invoice_table " +
"JOIN customer_table " +
"ON invoice_table.Debtor_Ser_No = customer_table.Customer_Serial " +
"WHERE invoice_table.Delivery_Status = :deliveryStatus " +
"GROUP BY customer_table.Customer_Serial " +
"ORDER BY customer_table.Customer_Sort, invoice_table.Invoice_Sort")
abstract public LiveData<List<CustomerInvoice>> getCustomerInvoices(int deliveryStatus);
*/
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract public void insert(Invoice... invoice);
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract public void insertCustomer(Customer... customer);
#Update(onConflict = OnConflictStrategy.IGNORE)
abstract public void updateInvoice(Invoice... invoices);
#Query("SELECT customer_table.* " + /* Room does not use the invoice table columns, they are not needed */
"FROM customer_table " +
"JOIN invoice_table ON invoice_table.customer_serial = customer_table.Customer_Serial " +
"WHERE invoice_table.Delivery_Status = :deliveryStatus " +
"GROUP BY customer_table.Customer_Serial ORDER BY customer_table.Customer_Sort, invoice_table.Invoice_Sort")
abstract public List<Customer> getApplicableCustomers(int deliveryStatus);
#Query("SELECT * FROM invoice_table WHERE delivery_status=:deliveryStatus AND customer_serial=:customerSerial ORDER BY invoice_sort" )
abstract List<Invoice> getApplicableInvoicesForCustomer(int deliveryStatus, int customerSerial);
#Transaction /* do in a single transaction */
#Query("") /* trick room so it applies transaction processing logic*/
public List<CustomerInvoice> getCustomerInvoices(int deliveryStatus) {
ArrayList<CustomerInvoice> rv = new ArrayList<>();
for(Customer c: getApplicableCustomers(deliveryStatus)) {
CustomerInvoice ci = new CustomerInvoice();
ci.customer = c;
ci.invoices = getApplicableInvoicesForCustomer(deliveryStatus,c.getCustomerSerial());
rv.add(ci);
}
return rv;
}
}
getCustomerInvoices method commented out
updateInvoice method added
getApplicableCustomers method added
similar to getCustomerInvoices but only gets the Customer fields/column as Room doesn't use the Invoice (the SQL could well be trimmed accordingly).
Instead of Debtor_Ser_No customer_serial hard coded.
getApplicableInvoicesForCustomer method added (for getting the appropriate invoices)
getCustomerInvoices method replaced using a method with body that gets the Customers, as Room does, but then gets the required children (invoices). This combining the getApplicable?? methods and returning the List of CustomerInvoice objects.
To demonstrate a modified Test, that a) updates the invoice delivery_status values in the database and b) uses a method to log the returned CustomerInvoices which allows the required delivery status to be passed:-
Note for brevity the mainThread is used.
public class MainActivity extends AppCompatActivity {
TheDatabase db;
InvoiceDao dao;
private static final String TAG = "DBINFO";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = TheDatabase.getInstance(this);
dao = db.getInvoiceDao();
Invoice invoice1 = new Invoice(1234, 1);
Invoice invoice2 = new Invoice(1235, 1);
Invoice invoice3 = new Invoice(2468, 2);
Invoice invoice4 = new Invoice(2469, 2);
Customer customer1 = new Customer(1, "Customer 1");
Customer customer2 = new Customer(2, "Customer 2");
dao.insertCustomer(customer1);
dao.insertCustomer(customer2);
dao.insert(invoice1);
dao.insert(invoice2);
dao.insert(invoice3);
dao.insert(invoice4);
logCustomerInvoices(0,"_R1");
logCustomerInvoices(1,"_R2");
invoice1.setDeliveryStatus(0);
invoice2.setDeliveryStatus(0);
invoice3.setDeliveryStatus(0);
invoice4.setDeliveryStatus(0);
dao.updateInvoice(invoice1);
dao.updateInvoice(invoice2);
dao.updateInvoice(invoice3);
dao.updateInvoice(invoice4);
logCustomerInvoices(0,"_R3");
logCustomerInvoices(1,"_R4");
invoice1.setDeliveryStatus(1);
invoice2.setDeliveryStatus(0);
invoice3.setDeliveryStatus(0);
invoice4.setDeliveryStatus(0);
dao.updateInvoice(invoice1);
dao.updateInvoice(invoice2);
dao.updateInvoice(invoice3);
dao.updateInvoice(invoice4);
logCustomerInvoices(0,"_R5");
logCustomerInvoices(1,"_R6");
}
void logCustomerInvoices(int deliveryStatus, String tagSuffix) {
for(CustomerInvoice ci: dao.getCustomerInvoices(deliveryStatus)) {
Log.d(TAG+tagSuffix,"Customer is " + ci.customer.getCustomerName() +
" Serial is " + ci.customer.getCustomerSerial() +
" Sort is " + ci.customer.getCustomerSort() + " There are " + ci.invoices.size() + " Invoices. They are ");
for (Invoice i: ci.invoices) {
Log.d(TAG+tagSuffix,"\n\tInvoice # is " + i.getInvoiceNumber() + " CustSerial is " + i.getCustomerSerial() + " DlvrStatus is " + i.getDeliveryStatus() + " Sort is " + i.getInvoiceSort());
}
}
}
}
When run 6 sets (3 pairs) of results may be output to the log. The output being:-
2023-02-04 06:53:16.867 D/DBINFO_R1: Customer is Customer 1 Serial is 1 Sort is 1675453996772-1 There are 2 Invoices. They are
2023-02-04 06:53:16.868 D/DBINFO_R1: Invoice # is 1234 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996766-1234
2023-02-04 06:53:16.868 D/DBINFO_R1: Invoice # is 1235 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996771-1235
2023-02-04 06:53:16.869 D/DBINFO_R1: Customer is Customer 2 Serial is 2 Sort is 1675453996772-2 There are 2 Invoices. They are
2023-02-04 06:53:16.869 D/DBINFO_R1: Invoice # is 2468 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2468
2023-02-04 06:53:16.869 D/DBINFO_R1: Invoice # is 2469 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2469
2023-02-04 06:53:16.887 D/DBINFO_R3: Customer is Customer 1 Serial is 1 Sort is 1675453996772-1 There are 2 Invoices. They are
2023-02-04 06:53:16.887 D/DBINFO_R3: Invoice # is 1234 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996766-1234
2023-02-04 06:53:16.887 D/DBINFO_R3: Invoice # is 1235 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996771-1235
2023-02-04 06:53:16.887 D/DBINFO_R3: Customer is Customer 2 Serial is 2 Sort is 1675453996772-2 There are 2 Invoices. They are
2023-02-04 06:53:16.887 D/DBINFO_R3: Invoice # is 2468 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2468
2023-02-04 06:53:16.887 D/DBINFO_R3: Invoice # is 2469 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2469
2023-02-04 06:53:16.906 D/DBINFO_R5: Customer is Customer 1 Serial is 1 Sort is 1675453996772-1 There are 1 Invoices. They are
2023-02-04 06:53:16.906 D/DBINFO_R5: Invoice # is 1235 CustSerial is 1 DlvrStatus is 0 Sort is 1675453996771-1235
2023-02-04 06:53:16.906 D/DBINFO_R5: Customer is Customer 2 Serial is 2 Sort is 1675453996772-2 There are 2 Invoices. They are
2023-02-04 06:53:16.906 D/DBINFO_R5: Invoice # is 2468 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2468
2023-02-04 06:53:16.906 D/DBINFO_R5: Invoice # is 2469 CustSerial is 2 DlvrStatus is 0 Sort is 1675453996771-2469
2023-02-04 06:53:16.911 D/DBINFO_R6: Customer is Customer 1 Serial is 1 Sort is 1675453996772-1 There are 1 Invoices. They are
2023-02-04 06:53:16.911 D/DBINFO_R6: Invoice # is 1234 CustSerial is 1 DlvrStatus is 1 Sort is 1675453996766-1234
R1 (as delivery status's are all 0 returns 2 Customers with 2 Invoices each)
R2 returns nothing as there are no Customers with invoices with a delivery status of 1
The invoice updates do nothing as the status is already 0, so:-
R3 returns all
R4 nothing
As 1 invoice is changed to have a delivery status of 1 then
R5 returns 2 Customer but the first with 1 invoice (as expected) and the other with 2 invoices as expected
R6 returns 1 Customer with 1 Invoices who's status is 1
Customers and Invoices sorted accordingly.
Related
I have two tables in sqlite in android:
Table1: Outlets
id pid OutletName Status
1 1 Tesco 1
2 1 Coors 1
3 2 Coors 1
Table2: projectparams
ppid projectname outletname param projStatus
1 RA_Pepsi Tesco shelfstrip 2
2 RA_Pepsi Tesco shelfleft 2
3 RA_Cocola Coors shelfstrip 1
Table1: For every PID (ProjectID) there are multiple Outlets stored in OutletName.
Table2: For each outlet there are multiple params stored in project params.
Whenever the user completes a task the param in Table2 is updated to 2. In the above example, when the user completes two tasks in Tesco, the status would be updated to 2 upon completion.
I am trying to set the outlets status to 2 based on completion of tasks for every outlet. In this example, when the status is updated to 2 in projectparam table, I want the Status in Outlets to be updated to 2 for the outlet Tesco.
I am using the following codes:
triggered when the task gets completed.
private void updateTaskStatus() {
SQLiteDatabase db = sqLiteHelper.getWritableDatabase();
String spinTextRecovered = spnTxt.getString("spinText", null);
String updateTable = "update projectparams set projStatus=2 where param='"+spinTextRecovered+"' and projectName='"+projectName+"' and OutletName='"+outletName+"'";
db.execSQL(updateTable);
takePicButton.setEnabled( false );
}
Triggered every time when the task gets completed and updates the outlet status when all the tasks for an outlet is completed.
private void updateOutletStatus() {
String query = "select OutletName from projectparams where projectName= '"+projectName+"' and OutletName= '"+outletName+"' group by projStatus";
if (sqLiteHelper.getData(query).getCount()==1) {
Toast.makeText( getApplicationContext(), "No more tasks in this outlet!", Toast.LENGTH_SHORT ).show();
SQLiteDatabase db = sqLiteHelper.getWritableDatabase();
String outletUpdate = "update outlets set Status=2 where OutletName='"+outletName+"' and pid = (select id from projects where ProjectName = '"+projectName+"' ) ";
db.execSQL(outletUpdate);
}
}
The above code works...however since I am using an intent to display the projects, outlets and task parameters, many times i find the updation of outlet not happening.
Can i write this better? Can someone guide me to use triggers?
I believe your core issue is that the query
String query = "select OutletName from projectparams where projectName= '"+projectName+"' and OutletName= '"+outletName+"' group by projStatus";
Will return 1 not only if all the projects have completed but also if none have been completed (i.e. all the projStatus values are 1).
Additionally, as it stands if very much appears that the Outlets table is superfluous. That is all columns (bar the id, which serves no purpose) are duplicated in the projectparams table.
You can ascertain is all the tasks for a project by multiplying the number of params by 2 (the status completion value) against the sum of all of the projstatus values. If they match then all params have been set to 2.
Consider the following which is your data (but status 1 for RA_Perpsi/Tesco rows) plus some additional data where the only fully completed is for the Completed Example project (highlighted) as per :-
The using (to process all results)
-- WHERE clause removed group by projectname added (to show all results)
SELECT OutletName FROM projectparams GROUP BY projectname,projStatus;
The result is :-
That is Ra_Pepsi/Tesco has no params completed (nothing done) but there is only one row so the project is detected as being completed. Likewise for RA_Cocola and for Some other outlet.
Completed Example produces 1 row so is complete as it should be. Incomplete example (where it is part way through) is the only one that comes up as incomplete.
Using the above data consider the following (noting that no reference is needed to the Outlets table AND that the WHERE clause has been omitted and the GROUP BY according to projectname to show all projects) :-
SELECT
OutletName,
CASE
WHEN (count() * 2 - sum(projStatus)) = 0 THEN 2 ELSE 1
END AS Status,
(count() * 2 - sum(projStatus)) ||' of '||count() AS outstanding
FROM projectparams
GROUP BY projectname
This results in :-
As such there is no need for the Outlet Table the projectparams table, at least according to your example, is sufficient and as such there is no need for a trigger. Whenever an update is made you simply refresh the display using the one table and a query such as the last example.
To demonstrate your scenario step by step consider the following code (based upon the data above i.e. the RA_Pepsi has been added but nothing done) :-
-- First Query
SELECT
OutletName,
CASE
WHEN (count() * 2 - sum(projStatus)) = 0 THEN 2 ELSE 1
END AS Status,
(count() * 2 - sum(projStatus)) ||' of '||count() AS outstanding
FROM projectparams
WHERE projectname = 'RA_Pepsi'AND outletName = 'Tesco'
GROUP BY projectname
;
-- First Update
UPDATE projectparams SET projStatus = 2 WHERE param = 'shelfstrip' AND projectname = 'RA_Pepsi' AND outletName = 'Tesco';
-- 2nd Query
SELECT
OutletName,
CASE
WHEN (count() * 2 - sum(projStatus)) = 0 THEN 2 ELSE 1
END AS Status,
(count() * 2 - sum(projStatus)) ||' of '||count() AS outstanding
FROM projectparams
WHERE projectname = 'RA_Pepsi'AND outletName = 'Tesco'
GROUP BY projectname
;
-- 2nd Update (all completed)
UPDATE projectparams SET projStatus = 2 WHERE param = 'shelfleft' AND projectname = 'RA_Pepsi' AND outletName = 'Tesco';
-- 3rd Query
SELECT
OutletName,
CASE
WHEN (count() * 2 - sum(projStatus)) = 0 THEN 2 ELSE 1
END AS Status,
(count() * 2 - sum(projStatus)) ||' of '||count() AS outstanding
FROM projectparams
WHERE projectname = 'RA_Pepsi'AND outletName = 'Tesco'
GROUP BY projectname
;
The first query shows the project with nothing done as per :-
The 2nd query (after changing the status of the shelfstrip to 2) shows :-
The 3rd query (after changing the status of the shelfleft to 2) shows :-
For Android
The foloowing code demonstrates applying the above on Android and additional uses the recommended convenience methods (they build much of the SQL, off protection against SQL injection and add additional functionality e.g. update returns the number of rows updated) :-
Two methods (the first using the query above, the second updating as per yours but using the update convenience method :-
public String getOutletStatus(String projectname, String outletname) {
String rv = "Ooops nothing found!"; // default if nothing found
String[] columns = new String[]{"outletname",
"CASE " +
"WHEN (count() * 2 - sum(projStatus)) = 0 THEN 2 ELSE 1 " +
"END AS Status", //<<<<<<<<< the column name will be Status
"(count() * 2 - sum(projStatus)) ||' of '||count() AS outstanding" // The column name will be outstanding
};
String whereclause = "projectname=? AND outletName=?";
String[] whereargs = new String[]{projectname,outletname};
String groupby = "projectname"; // not needed
Cursor csr = sqliteHelper.getWritableDatabase().query("projectparams",columns,whereclause,whereargs,null,null,null);
if (csr.moveToFirst()) {
int statuscode = csr.getInt(csr.getColumnIndex("Status"));
String outstanding = csr.getString(csr.getColumnIndex("outstanding"));
String outlet = csr.getColumnName(csr.getColumnIndex("outletname"));
String statusdescription = "incomplete";
if (statuscode == 2) {
statusdescription = "complete";
}
rv = "The status of project " + projectname + " for outlet " + outlet + " is " + statusdescription + ". With " + outstanding + ".";
}
csr.close();
return rv;
}
public void updateStatus(String projectname, String outletname, String param) {
String whereclause = "projectname=? AND outletname=? AND param=?";
String[] whereargs = new String[]{projectname,outletname,param};
ContentValues cv = new ContentValues();
cv.put("projStatus",2);
int number_of_rows_updated = sqliteHelper.getWritableDatabase().update("projectparams",cv,whereclause,whereargs);
}
They have been tested (using the base data show above) and reflect the final 3 queries with updates between them :-
String status = getOutletStatus("RA_Pepsi","Tesco"); // Before anything
Log.d("RESULT",status);
updateStatus("RA_Pepsi","Tesco","shelfstrip"); //shelfstrip completed
status = getOutletStatus("RA_Pepsi","Tesco");
Log.d("RESULT",status);
updateStatus("RA_Pepsi","Tesco","shelfleft"); //shelfleft completed
status = getOutletStatus("RA_Pepsi","Tesco");
Log.d("RESULT",status);
Result in the Log :-
04-29 12:46:09.615 20471-20471/? D/RESULT: The status of project RA_Pepsi for outlet outletname is incomplete. With 2 of 2.
04-29 12:46:09.621 20471-20471/? D/RESULT: The status of project RA_Pepsi for outlet outletname is incomplete. With 1 of 2.
04-29 12:46:09.625 20471-20471/? D/RESULT: The status of project RA_Pepsi for outlet outletname is complete. With 0 of 2.
Trying to learn Android studio. And I expect your help on this.I am adding and listing data with sqlite.
for example;
id - name - value
1 - john - 100
2 - mark - 200
3 - john - 150
4 - john - 200
5 - adam - 400
what I want to do, list only names one time.
1 - john
2 - mark
3 - adam
private void showlist() {
ArrayList<DataListItems> contactList = new ArrayList<DataListItems>();
contactList.clear();
String query = "SELECT * FROM data ";
Cursor c1 = sqlHandler.selectQuery(query);
if (c1 != null && c1.getCount() != 0) {
if (c1.moveToFirst()) {
do {
DataListItems contactListItems = new DataListItems();
contactListItems.setid(c1.getString(c1
.getColumnIndex("id")));
contactListItems.setName(c1.getString(c1
.getColumnIndex("name")));
contactListItems.setValue(c1.getString(c1
.getColumnIndex("value")));
contactList.add(contactListItems);
} while (c1.moveToNext());
}
}
c1.close();
DataListAdapter contactListAdapter = new DataListAdapter(
SiteList.this, contactList);
lvCustomList.setAdapter(contactListAdapter);
}
You can use the GROUP BY name to select only one name. However, the id selected would be an arbitrary id for each group (name).
your code could use String query = "SELECT * FROM data GROUP BY name";
If you wanted the first/lowest id per name then you could use min(id) in conjunction with GROUP BY NAME.
your code could use String query = "SELECT min(id) AS id, name, value FROM data GROUP BY name";
You say that your expected result should be
1 - john
2 - mark
3 - adam
That would be more complicated (and perhaps of little use) as the id for adam is 5 not 3 (I assume that is simply a typing error).
I am using Room (1.0.0.rc1) with RX, my Dao is defined is this way:
#Dao
interface AccountDao {
#Query("SELECT * FROM Account ORDER BY name")
fun all(): Flowable<List<Account>>
}
I am subscribing this way:
dao
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { accounts = it }
I have more than one place in the code who subscribes to the flowable. The first to subscribe gets the data, the other ones don't.
How can I make an observable that will emit the actual content every time someones subscribes and will also notify every subscriber when the data changes?
You can use replay to emit lastest value every time someone subscribes. And use distinctUntilChanged to notify only when data changes.
Here is the sample:
import io.reactivex.Observable;
import io.reactivex.subjects.BehaviorSubject;
public class Q47000608 {
public static void main(String[] args) {
BehaviorSubject<Integer> bs = BehaviorSubject.createDefault(1);
Observable<Integer> o = bs.replay(1).autoConnect().distinctUntilChanged();
o.subscribe(i -> System.out.println("s1 accept " + i));
bs.onNext(2);
o.subscribe(i -> System.out.println("s2 accept " + i));
o.subscribe(i -> System.out.println("s3 accept " + i));
bs.onNext(3);
o.subscribe(i -> System.out.println("s4 accept " + i));
bs.onNext(4);
}
}
And output:
s1 accept 1
s1 accept 2
s2 accept 2
s3 accept 2
s1 accept 3
s2 accept 3
s3 accept 3
s4 accept 3
s1 accept 4
s2 accept 4
s3 accept 4
s4 accept 4
SELECT * FROM EVENT_total_time
WHERE USER_ID = 2
AND LEVEL_SEQUENCE_NUMBER = 1
AND DATETIME( TIMESTAMP ) BETWEEN DATETIME('now','-7 days') AND DATETIME('now')
ORDER BY datetime( TIMESTAMP ) ASC
and the DB has
PRINTING TABLE EVENT_total_time
0 {
_id=56
USER_ID=2
TIMESTAMP=2016-02-15 21:29:29
EPOCH_TIME=1455571769621
NEW_VALUE=3.034
LEVEL_ID=7
LEVEL_SEQUENCE_NUMBER=1
EXTRAS={"old_value":0}
}
1 {
_id=57
USER_ID=2
TIMESTAMP=2016-02-15 21:29:29
EPOCH_TIME=1455571769822
NEW_VALUE=3.219
LEVEL_ID=7
LEVEL_SEQUENCE_NUMBER=1
EXTRAS={"old_value":3.034}
}
this query was ran 30sec ago so ... datetime('now') should be 15.02.2016 21:32:00 ....
I ran this query and it produced an empty result. Can someone spot why?
I am using greendao or sqlite and my model (User) has a DOB column which stores the birthdate of the user. How can I find the users which have birthday between a range of dates?
I am using following query in mysql but cannot find a way to do this using sqlite:
SELECT *
FROM user
WHERE datediff( MAKEDATE( YEAR(NOW()) , DAYOFYEAR(dob) ) , NOW())
BETWEEN 0 AND 7
OR datediff( MAKEDATE( YEAR(NOW())+1 , DAYOFYEAR(dob) ) , NOW())
BETWEEN 0 AND 7
OR datediff( MAKEDATE( YEAR(NOW())-1, DAYOFYEAR(dob) ), NOW())
BETWEEN 0 AND 7;
Like SQLite documentation says, there is no Date format in SQLite (http://www.sqlite.org/datatype3.html).
So, greenDAO uses timestamp to save Dates. Maybe you are looking for this kind of query:
Date startRangeDate = new Date();
Date endRangeDate = new Date();
DeveloperDao targetDao = daoSession.getDeveloperDao();
QueryBuilder<Developer> qb = targetDao.queryBuilder();
qb.where(Properties.Birthday.between(startRangeDate.getTime(), endRangeDate.getTime()));
After lot of trying, I finally came up with this solution. I am posting this so it may help somebody in future:
public static List<Customer> getThisWeekCustomersByBirthday(Context context,int limit, int offset) {
String whereQuery = " julianday(strftime('%Y','now','localtime')||strftime('-%m-%d',datetime(dob/1000, 'unixepoch'),'localtime')) between julianday(strftime('%Y-%m-%d','now','weekday 0','-7 days','localtime')) and julianday(strftime('%Y-%m-%d','now','weekday 0','localtime')) "
+ "OR julianday(strftime('%Y','now','+1 year','localtime')||strftime('-%m-%d',datetime(dob/1000, 'unixepoch'),'localtime')) between julianday(strftime('%Y-%m-%d','now','weekday 0','-7 days','localtime')) and julianday(strftime('%Y-%m-%d','now','weekday 0','localtime')) "
+ "OR julianday(strftime('%Y','now','-1 year','localtime')||strftime('-%m-%d',datetime(dob/1000, 'unixepoch'),'localtime')) between julianday(strftime('%Y-%m-%d','now','weekday 0','-7 days','localtime')) and julianday(strftime('%Y-%m-%d','now','weekday 0','localtime')) ";
Query query = getCustomerDao(context).queryBuilder().where(
new WhereCondition.StringCondition(whereQuery)).limit(limit).offset(offset).build();
return query.list();
}