Sqlite: replacing or updating a row only if it is changed - ios

An Objective-C iOS app integrates a sqlite with a set of rows, each identified by an ID. For example:
| id | user_name | age |
------------------------------
| 1 | johnny | 33 |
| 2 | mark | 30 |
| 3 | maroccia | 50 |
Asynchronously, the app receives the same set of records, but some of them are modified: it has to update (or replace) only the modified records, ignoring the other ones (those not modified).
For example, the app receives such updated rows:
| id | user_name | age |
------------------------------
| 1 | johnny | 33 |
| 2 | mark | 30 |
| 3 | ballarin | 50 | <------ CHANGED RECORD
In this case, only the third record is changed and the app should update or replace just it, ignoring the first two.
Obviously, the INSERT OR REPLACE does not suit me because it will write all the records. So, there exists some procedure in sqlite (or Objective-C) which can help me, updating only the modified records?
Thanks

You could simply replace all rows; the result is the same.
If you do not want to rewrite rows that have not actually changed, you have to compare all column values. If you have both the old rows and the received rows in separate tables, you can compare entire rows with a compound query:
INSERT OR REPLACE INTO MyData
SELECT * FROM ReceivedData
EXCEPT
SELECT * FROM MyData;

Related

Rails: create unique auto-incremental id based on sibling records

I have three models in my rails project, namely User, Game, Match
user can create many matches on each game
so table structure for matches is like
table name: game_matches
+----+---------+---------+-------------+------------+
| id | user_id | game_id | match_type | match_name |
+----+---------+---------+-------------+------------+
| 1 | 1 | 1 | practice | |
| 2 | 3 | 2 | challenge | |
| 3 | 1 | 1 | practice | |
| 4 | 3 | 2 | challenge | |
| 5 | 1 | 1 | challenge | |
| 6 | 3 | 2 | practice | |
+----+---------+---------+-------------+------------+
i want to generate match_name based on user_id, game_id and match_type values
for example match_name should be create like below
+----+---------+---------+-------------+-------------+
| id | user_id | game_id | match_type | match_name |
+----+---------+---------+-------------+-------------+
| 1 | 1 | 1 | practice | Practice 1 |
| 2 | 3 | 2 | challenge | Challenge 1 |
| 3 | 1 | 1 | practice | Practice 2 |
| 4 | 3 | 2 | challenge | Challenge 2 |
| 5 | 1 | 1 | challenge | Challenge 1 |
| 6 | 3 | 2 | practice | Practice 1 |
+----+---------+---------+-------------+-------------+
How can i achieve this auto incremental value in my rails model during new record creation.
Any help suggestions appreciated.
Thanks in advance.
I see two ways you can solve this:
DB: trigger
Rails: callback
Trigger (assuming Postgres):
DROP TRIGGER IF EXISTS trigger_add_match_name ON customers;
DROP FUNCTION IF EXISTS function_add_match_name();
CREATE FUNCTION function_add_match_name()
RETURNS trigger AS $$
BEGIN
NEW.match_name := (
SELECT
CONCAT(game_matches.match_type, ' ', COALESCE(count(*), 0))
FROM game_matches
WHERE game_matches.user_id = NEW.user_id AND game_matches.match_type = NEW.match_type
);
RETURN NEW;
END
$$ LANGUAGE 'plpgsql';
CREATE TRIGGER trigger_add_match_name
BEFORE INSERT ON game_matches
FOR EACH ROW
EXECUTE PROCEDURE function_add_match_name();
Please note that this is not tested.
Rails
class GameMatch
before_create :assign_match_name
private
def assign_match_name
number = GameMatch.where(user_id: user_id, match_type: match_type).count || 0
name = "#{match_type} #{number + 1}"
self.match_name = name
end
end
Again, untested.
I'd prefer the trigger solution since callbacks can be skipped or ommited altogether when inserting via pure SQL.
Also I'd add "match_number" column instead of the full name and then construct the name within the Model or a Decorator or a view Helper (more flexible, I18n) but the logic behind stays the same.
You should retrieve the last match_name for these user and game, split it, increase the counter and join back with a space. Unfortunately, SQL does not provide SPLIT function, so somewhat like below would be a good start:
SELECT match_name
FROM match_name
WHERE user_id = 3
AND game_id = 2
ORDER BY id DESC
LIMIT 1
I would actually better create a match_number column of type INT to keep the number by type and produce a name by concatenation the type with this number.

In data warehouse, can fact table contain two same records?

If a user ordered same product with two different order_id;
The orders are created within a same date-hour granularity, for example
order#1 2019-05-05 17:23:21
order#2 2019-05-05 17:33:21
In the data warehouse, should we put them into two rows like this (Option 1):
| id | user_key | product_key | date_key | time_key | price | quantity |
|-----|----------|-------------|----------|----------|-------|----------|
| 001 | 1111 | 22 | 123 | 456 | 10 | 1 |
| 002 | 1111 | 22 | 123 | 456 | 10 | 2 |
Or just put them in one row with the aggregated quantity (Option 2):
| id | user_key | product_key | date_key | time_key | price | quantity |
|-----|----------|-------------|----------|----------|-------|----------|
| 001 | 1111 | 22 | 123 | 456 | 10 | 3 |
I know if I put the order_id as a degenerate dimension in the fact table, it should be Option 1. But in our case, we don't really want to keep the order_id.
Also I once read an article that says that when all dimensions are filtered out, there should be only one row of data in the fact table. If this statement is correct, the Option 2 will be the choice.
Is there a principle where I can refer ?
Conceptually, fact tables in a data warehouse should be designed at the most detailed grain available. You can always aggregate data from the lower granularity to the higher one, while the opposite is not true - if you combine the records, some information is lost permanently. If you ever need it later (even though you might not see it now), you'll regret the decision.
I would recommend the following approach: in a data warehouse, keep order number as degenerate dimension. Then, when you publish a star schema, you might build a pre-aggregated version of the table (skip order number, group identical records by date/hour). This way, you can have smaller/cleaner fact table in your dimensional model, and yet preserve more detailed data in the DW.

Influx: doing math the same fields in different groups

I have InfluxDB measurement currently set up with following "schema":
+----+-------------+-----------+
| ts | cost(field) | type(tag) |
+----+-------------+-----------+
| 1 | 10 | 'a' |
| 1 | 20 | 'b' |
| 2 | 12 | 'a' |
| 2 | 18 | 'b' |
| 2 | 22 | 'c' |
+------------------+-----------+
I am trying to write a query that will group my table by timestamp and get a delta between field values of two different tags. If I want to get delta between tag 'a' and tag 'b', it will give me following result (please not that I ignore tag 'c'):
+----+-----------+------------+
| ts | type(tag) | delta_cost |
+----+-----------+------------+
| 1 | 'a' | 10 |
| 2 | 'b' | 6 |
+----+-----------+------------+
Is it something Influx can do or am I using the wrong tool?
Just managed to answer my own question. While one of the obvious ways would be performing self-join, Influx does not support joins anymore. We can, however, use nested selects in a following format:
SELECT MEAN(cost_a) - MEAN(cost_b) as delta_cost
FROM
(SELECT cost as cost_a, tag, tablename where tag='a'),
(SELECT cost as cost_b, tag, tablename where tag='b')
GROUP BY time(60s)
Since I am getting my data every 60 seconds anyway, and I have a guarantee of just one point per tag per 60 seconds, I can use GROUP BY and take MEAN without any problems

How to perform batch update of a column within a range in psql

Here is my table structure:
Table name:Items
--------------------------------
id | category_id | code |
--------------------------------
1 | 1 | 15156 |
2 | 1 | 15157 |
2 | 1 | 15158 |
2 | 1 | 15159 |
2 | 1 | 15160 |
2 | 1 | 15161 |
Here code field is unique and its type is string. I need to increment code field values by +1(code field is string).
You can try
Item.update_all(code: "#{code.to_i + 1}")
If you want to read update_all
The update_all won't work because the record attributes are not available.
Better might be...
minimum = "15157"
maximum = "15160"
Item.where("code >= ? AND code <= ?", minimum, maximum).each{|i| i.update_attribute(:code, "#{i.code.to_i + 1}") }
(edited to reflect two arguments passed to update_attribute)
Edited to reflect #rustamagasanov suggestion to limit to a given range of code values...

Designing a Core Data managed object model for an iOS app that creates dynamic databases

I'm working on an iPhone app for users to create mini databases. The user can create a custom database schema and add columns with the standard data types (e.g. string, number, boolean) as well as other complex types such as objects and collections of a data type (e.g. an array of numbers).
For example, the user can create a database to record his meals.
Meal database:
[
{
"timestamp": "2013-03-01T13:00:00",
"foods": [1, 2],
"location": {
"lat": 47.253603,
"lon": -122.442537
}
}
]
Meal-Food database:
[
{
"id": 1,
"name": "Taco",
"healthRating": 0.5
},{
"id": 2,
"name": "Salad",
"healthRating": 0.8
}
]
What is the best way to implement a database for an app like this?
My current solution is to create the following database schema for the app:
When the user creates a new database schema as in the example above, the definition table will look like this:
+----+-----------+--------------+------------+-----------------+
| id | parent_id | name | data_type | collection_type |
+----+-----------+--------------+------------+-----------------+
| 1 | | meal | object | |
| 2 | 1 | timestamp | timestamp | |
| 3 | 1 | foods | collection | list |
| 4 | 1 | location | location | |
| 5 | | food | object | |
| 6 | 5 | name | string | |
| 7 | 5 | healthRating | number | |
+----+-----------+--------------+------------+-----------------+
When the user populates the database, the record table will look like this:
+----+-----------+---------------+------------------------+-----------+-----+
| id | parent_id | definition_id | string_value | int_value | ... |
+----+-----------+---------------+------------------------+-----------+-----+
| 1 | | 1 | | | |
| 2 | 1 | | 2013-03-01T13:00:00 | | |
| 3 | 1 | 2 | | 1 | |
| 4 | 1 | 2 | | 2 | |
| 5 | 1 | 4 | 47.253603, -122.442537 | | |
+----+-----------+---------------+------------------------+-----------+-----+
More details about this approach:
Values for different data types are stored in different columns in the record table. It is up to the app to parse values correctly (e.g. converting timestamp int_value into a date object).
Constraints and validation must be performed on the app as it is not possible on the database level.
What are other drawbacks with this approach and are there better solutions?
First of all your Record table is very inefficient and somewhat hard to work with. Instead you can have separate record tables for each record type you need to support.It will simplify everything a lot and add some additional flexibility, because it will not be a problem to introduce support for a new record type.
With that being said we can conclude it will be enough to have basic table management to make your system functional. Naturally, there is ALTER TABLE command:
but in some cases it might be very expensive and some engines have various limitations. For example:
SQLite supports a limited subset of ALTER TABLE. The ALTER TABLE
command in SQLite allows the user to rename a table or to add a new
column to an existing table.
Another approach might be to use BLOBs with some type tags in order to store record values.
This approach will reduce the need to support separate tables. It leads us to Schemaless approach.
Do you absolutely have to use CoreData for this?
It might make more sense to use a schema-less solution, such as http://developer.couchbase.com/mobile/develop/references/couchbase-lite/release-notes/iOS/index.html

Resources