SQLite INSERT OR REPLACE with condition - sqlite

I have a SQLite database and I'm inserting new records using this query:
INSERT OR REPLACE INTO tags (id, name, color, type, deleted, dirty) VALUES (?, ?, ?, ?, ?, 0)
Id is PRIMARY KEY
If I'm inserting a new record (id doesn't exists in the table) INSERT get executed and everything is fine. If the id already exists then the REPLACE kicks in and the record get replaced. I should add a condition to the REPLACE query: the record should be replaced if and only if dirty is set to 1 in the record already present in the table. How can I add such condition to the query?

This is what i use.
First attempt the update with your condition
then, if not updated, Perform the insert...
Check one example below. calling upsertCategory
/*
* Update a category
*/
public long updateCategory(Category category) {
ContentValues values = new ContentValues();
values.put(COL_CATEGORYDESCRIPTION, category.getDescription());
values.put(COL_CATEGORYSTATUS, category.getStatus());
values.put(COL_CATEGORYCREATEDAT, category.getCreatedAt().getDate());
values.put(COL_CATEGORYUPDATEDAT, category.getUpdatedAt().getDate());
long id = db.update(TABLE_CATEGORIES, values, KEY_ID + "=" + category.getId(), null);
return id;
}
/*
* Update or Insert a category
*/
public long upsertCategory(Category category) {
// update row
long id = updateCategory(category);
// 0 rows affected
if (id == 0) {
// insert row
id = insertCategory(category);
}
return id;
}

Related

SQLite - Inserting values from multiple rows in the same table into a single row in another table

I have a table of phone numbers (tblPhoneNumbers):
ID, UserID, PhoneNumber
and I need to move them into a Users table (tblUsers) that contains:
ID, PhoneNumber1, PhoneNumber2
tblPhoneNumbers is assumed to have 2 rows for every user. Is it possible to move the PhoneNumber value of the first row into PhoneNumber1, and the PhoneNumber value of the second row into PhoneNumber2?
Essentially this is reverse-normalization but this is the task I need help with.
Thanks!
I need to use SQLite so I cannot use any syntax not available to SQLite.
If you're using sqlite 3.25 or better, you can use window functions to do it all in one statement (I assume here that the UserID column from tblPhoneNumbers is a foreign key that references ID from tblUsers, and that the given userid already has a record in that table; adjust as needed):
WITH allnumbers AS
(SELECT UserID
, PhoneNumber
, row_number() OVER (PARTITION BY UserID) AS num
FROM tblPhoneNumbers)
UPDATE tblUsers AS t
SET PhoneNumber1 = (SELECT a.PhoneNumber
FROM allnumbers AS a
WHERE a.UserID = t.ID AND num = 1)
, PhoneNumber2 = (SELECT a.PhoneNumber
FROM allnumbers AS a
WHERE a.UserID = t.ID AND num = 2);
(And if your system only has an older version that don't support window functions, you can always download a copy of the latest version of the sqlite3 shell and use it instead of the OS provided one).
(edit: You'll want an index on tblPhoneNumbers.UserID for better performance)
You could use the following :-
-- Create a temporary swap table
CREATE TEMP TABLE IF NOT EXISTS swapPhoneNumbers (ID INTEGER PRIMARY KEY, UserID INTEGER, PhoneNumber TEXT, replacementPhoneNumber TEXT);
-- Clear the temporary swap table in case it's used more than once
DELETE FROM swapPhoneNumbers;
-- Populate the temporary swap table according to the original data
INSERT INTO swapPhoneNumbers (ID,UserID,PhoneNumber) SELECT * FROM tblPhoneNumbers;
-- Update the swap table to include the replacement phone numbers
UPDATE swapPhoneNumbers SET replacementPhoneNumber = (
SELECT PhoneNumber FROM tblPhoneNumbers
WHERE swapPhoneNumbers.userID = tblPhoneNumbers.userID
AND swapPhoneNumbers.ID <> tblPhoneNumbers.ID
);
-- Update the original table with the new phone numbers
UPDATE tblPhoneNumbers SET PhoneNumber = (
SELECT replacementPhoneNumber FROM swapPhoneNumbers
WHERE tblPhoneNumbers.ID = swapPhoneNumbers.ID
);
The following is the SQL used to test the above.
-- Create Testing Table with some data
DROP TABLE IF EXISTS tblphoneNumbers;
CREATE TABLE IF NOT EXISTS tblPhoneNumbers (ID INTEGER PRIMARY KEY, userID INTEGER, PhoneNumber TEXT);
INSERT INTO tblPhoneNumbers (userID, PhoneNumber) VALUES
(1,'0111111111'),(1,'0222222222'),(2,'0333333333'),(2,'0444444444'),(3,'0555555555'),(3,'0666666666')
;
-- Show what is in the original table
SELECT * FROM tblPhoneNumbers;
-- Create a temporary swap table
CREATE TEMP TABLE IF NOT EXISTS swapPhoneNumbers (ID INTEGER PRIMARY KEY, UserID INTEGER, PhoneNumber TEXT, replacementPhoneNumber TEXT);
-- Clear the temporary swap table in case it's used more than once
DELETE FROM swapPhoneNumbers;
-- Populate the temporary swap table according to the original data
INSERT INTO swapPhoneNumbers (ID,UserID,PhoneNumber) SELECT * FROM tblPhoneNumbers;
-- Show what is in the swap table
SELECT * FROM swapPhoneNumbers;
-- Update the swap table to include the replacement phone numbers
UPDATE swapPhoneNumbers SET replacementPhoneNumber = (
SELECT PhoneNumber FROM tblPhoneNumbers
WHERE swapPhoneNumbers.userID = tblPhoneNumbers.userID
AND swapPhoneNumbers.ID <> tblPhoneNumbers.ID
);
-- Show what is now in the swap table
SELECT * FROM swapPhoneNumbers;
-- Update the original table with the new phone numbers
UPDATE tblPhoneNumbers SET PhoneNumber = (
SELECT replacementPhoneNumber FROM swapPhoneNumbers
WHERE tblPhoneNumbers.ID = swapPhoneNumbers.ID
);
-- Show what is in the original table
SELECT * FROM tblPhoneNumbers;
And this is some screen shots from doing it

how to select particular column data in SQLite db in android studio?

I want to take data from table name "transactions" in my SQLite db and return it to main activity in android studio.
This is my DatabaseHelper.java class code to return balance from transactions table:
public int previousBal(String activeuser){
SQLiteDatabase db=this.getReadableDatabase();
Cursor cursor=db.rawQuery("select balance from transactions where email=? order by t_id desc limit 1",new String[]{activeuser});
int balance = cursor.getInt(Integer.parseInt("balance"));
return balance;
}
This is the MainActivity code. I am using previousBal method to get data from the table:
String bal;
int previousBal=db.previousBal(activeuser);
bal= String.valueOf(previousBal);
System.out.println("PREVIOUS BALANCE IS======"+bal);
This code does not return anything. help me figure it out.
The issue you have, assuming that the active user string is a valid exisitng user, is that you are trying to extract from the position that is effectively before the first row.
That is, you need to MOVE to a row in the Cursor.
Additionally the Cursor's getInt method expects the offset of the column from which to get the data, which would be 0. However, it is better (less prone to error and generally more flexible) to use the Cursor's getColumnIndex method to retrieve the column's offset.
As such change :-
public int previousBal(String activeuser){
SQLiteDatabase db=this.getReadableDatabase();
Cursor cursor=db.rawQuery("select balance from transactions where email=? order by t_id desc limit 1",new String[]{activeuser});
int balance = cursor.getInt(Integer.parseInt("balance"));
return balance;
}
to :-
public int previousBal(String activeuser){
SQLiteDatabase db=this.getReadableDatabase();
Cursor cursor=db.rawQuery("select balance from transactions where email=? order by t_id desc limit 1",new String[]{activeuser});
if (cursor.moveToFirst()) {
int balance = cursor.getInt(cursor.getColumnIndex("balance"));
} else {
balance = 0;
}
return balance;
}
This will move to the first row, if there is one, and extract the vale in the balance column of the cursor.
If there is no row that matches the query's selection criteria then the else clause will set the balance to 0.
If multiple rows are extracted the value from the first row will be returned.
Addtional
Frequently the use of the SQLiteDatabase rawQuery is frowned upon unless it is necessary. In you case the SQLitedatabase query method can be used. As such, the recommended previousBal method would be :-
public int previousBal(String activeuser){
SQLiteDatabase db=this.getReadableDatabase();
Cursor.query(
"transactions", // name of the table to query
new String[]{"balanace"}, // String array of columns to extract
"email=?", // WHERE clause (? indicates an arg)
new String[]{activeuser}, // The list of args to replace the ? (or ?'s on a sequential basis)
null, // GROUP BY clause
null, // HAVING clause
"t_id DESC", // ORDER clause
"1" // LIMIT value as a String
);
if (cursor.moveToFirst()) {
int balance = cursor.getInt(cursor.getColumnIndex("balance"));
} else {
balance = 0;
}
return balance;
}
The query convenience method builds the SQL escaping characters and offers improved protection against SQL injection.

Insert the newset value in a table and drop the old ones

I have a method which saves somes records for a machine in a table. However, I may insert by accident multiple times the records for the same machine in a day, so I want for this day to save the newest insert and drop the old ones. I thought one solution:
String sq = "SELECT date, idMachine "
+ "FROM MACHINES_RECORDS WHERE date = ? AND idMachine = ?";
try {
Class.forName(typeDB);
c = DriverManager.getConnection(path);
stm = c.prepareStatement(sq);
ResultSet rs = stm.executeQuery();
if (rs.next()) {
do {
//call an another method to drop this value
} while(rs.next());
}else {
//call an another method to insert this value
}
}catch (SQLException e) {
System.out.println(e.getMessage());
} finally {
if (stm != null)
stm.close();
if (c != null)
c.close();
}
Is there any better/smartest solution from this, so I can make all theses actions in the same method?
With a unique index (possibly the primary key) on date and idmachine you would use INSERT ... ON DUPLICATE KEY UPDATE, so as to insert when there is no entry for that machine on that day or update the existing record with the new data otherwise.
insert into machines_records
(machineid, date, data)
values
(#id, #date, #data)
on duplicate key update data = #data;
EDIT: I see you removed the MySQL tag. So you want an answer for SQLite. In SQLite you'd use INSERT OR REPLACE:
insert or replace into machines_records
(machineid, date, data)
values
(#id, #date, #data);

SQLite: Select one row or return NULL when no row satisfies conditions

I want to INSERT OR REPLACE a row specified by 'uuid' column value. There is what I have:
INSERT OR REPLACE INTO feature_layer (id, uuid, feature_collection)
VALUES ((SELECT id FROM feature_layer WHERE uuid=?) , ?, GeomFromGeoJSON(?));
Will this query do what I want, which means update row specified by uuid or insert new one if no such row is present?
Assuming that your id field is defined as (with AUTOINCREMENT)
id INTEGER PRIMARY KEY AUTOINCREMENT
the correct statement will be
INSERT OR REPLACE INTO feature_layer (id, uuid, feature_collection)
VALUES ((SELECT id FROM feature_layer WHERE uuid=?), ?, GeomFromGeoJSON(?));
(you missed one ( in your query)
If your id field is defined as (without AUTOINCREMENT)
id INTEGER PRIMARY KEY
you will want to use COALESCE to set a new id (I have added one ? for the new id) if no records are found:
INSERT OR REPLACE INTO feature_layer (id, uuid, feature_collection)
VALUES (COALESCE((SELECT id FROM feature_layer WHERE uuid=?),?) , ?, GeomFromGeoJSON(?));
With the above queries, as you want, you will update uuid and feature_collection when the id matches the one filtered by uuid, and if no records are found, then it will insert a new record with your desired values for uuid and feature_collection.
Anyway, take into account that your id field must be the PRIMARY KEY to use this method.

Create new sql entry with one value being MAX(Column)+1 gives Invalid use of group function

I want to manually create a ID field where I do MAX + 1, and I want to do it in one QUERY so I am certain 2 entries cant get the same field.
using (MySqlConnection dbConn = new MySqlConnection(ConfigurationManager.ConnectionStrings["ProjektConStr"].ConnectionString))
{
dbConn.Open();
using (MySqlCommand cmd = new MySqlCommand("INSERT INTO Submission (CaseId , SubjectId, CenterId, EmployeeName, Reason, Description, Explanation, Date, Done, ChiefLevel) VALUES (MAX(CaseId)+1, #subject_id, #center_id, #employee_name, #reason, #description, #explanation, #date, #done, #chief)", dbConn))
{
cmd.Parameters.AddWithValue("date", submission.Date);
cmd.Parameters.AddWithValue("subject_id", submission.SubjectId);
cmd.Parameters.AddWithValue("center_id", submission.CenterId);
cmd.Parameters.AddWithValue("employee_name", submission.EmployeeName);
cmd.Parameters.AddWithValue("reason", submission.Reason);
cmd.Parameters.AddWithValue("description", submission.Description);
cmd.Parameters.AddWithValue("explanation", submission.Explanation);
cmd.Parameters.AddWithValue("done", false);
cmd.Parameters.AddWithValue("chief", false);
cmd.ExecuteNonQuery();
}
}
Use subquery to select max and insert a value
INSERT INTO Submission (CaseId , SubjectId, CenterId, EmployeeName, Reason, Description, Explanation, Date, Done, ChiefLevel) VALUES (
(1 + coalesce((SELECT max(CaseId) FROM Submission), 0))
, #subject_id, #center_id, #employee_name, #reason, #description, #explanation, #date, #done, #chief)
using (MySqlCommand cmd = new MySqlCommand("INSERT INTO Submission
(CaseId , SubjectId, CenterId, EmployeeName, Reason, Description, Explanation,
Date, Done, ChiefLevel)
VALUES ((select MAX(CaseId)+1 from YourTable),
#subject_id, #center_id, #employee_name, #reason, #description, #explanation,
#date, #done, #chief)", dbConn))
You cannot do this in a single query, Either you have to fetch the MAX_id and then increment that, and insert or You can make the table as autoincrement, Here you dont have to include the id in the insert query.

Resources