Core Data or NSDictionary with multiple date entries (about 800+ each year)? What would be the most easy to implement? - objective-c

I'm trying to figure the best approach to solve this problem.
--
I have a "History" table that,
lists ALL years that have data.
If a user clicks a given Year, it segues to a new Table and,
lists ALL months that have data.
Clicking a given month, shows a new table that,
lists ALL days that have data.
Clicking a specific day, shows a list of one or multiple Time Stamps.
--
What is the best approach to solve this?
If user creates a Time Stamp. I need to insert it with today's date.
I also need to have the ability that if a user,
Deletes a given year. Everything in that year is deleted.
That same way,
Deleting a month, deletes everything in that month, for it's particular year.
And so on, to the point where the user should be able to delete Individual Time Stamps.
--
I thought I would Use a Dictionary with key for the "year". 2012, 2013, ...
And each retrieving another Dictionary with key for the "month", 1, 2, 3, 4, ...
And so on ... and so on ...
I also thought I could make a model using Core Data.
A Class Year representing the "Year" entity, having a relation to many possible Months, and each month, having a relation to many possible days, and days to Time *Stamps*.
And last,
I thought of creating a model with only two Entities.
Entries, with only one attribute "Date", that has a to-many relationship to "Time Stamps", receiving All the possible Time Stamps for that given day.
I am new to iOS programming. So this is all theory for me. But I did follow some Core Data tutorials and others working with NSDictionaries, protocols delegates and so on.
The "Dig In" approach as I go trough, seems more elegant. Specially because I think I could delete a particular given object in a cascade manner?
Do any of these make sense? Or is there a more obvious easy way to go about it? Also, please consider in the answer what would be easier to implement if a user chooses to delete a given entry in the "tree"
Any help is most appreciated.
Thank you advance!
Nuno

If you are going to rely on Core Data or any database engine, the best way to solve this is to use the database itself.
I see two possible solutions (there is more of course). The first, the simplest :
Entity
- timestamp
- year
- month
- day
- all_the_stuff_you_need
Make year, month and day readonly, updated along timestamp. Indexes: year, year+month, year+month+day. Easy call.
That way, you can very simple query the database, asking it to return the entities you need and only the entities you need.
A more complex setup would be:
Entity
- timestamp
- all_the_stuff_you_need
- year -> Year
- month -> Month
- day -> Day
Year
- year
- entities ->> Entity
Month
- month
- entities ->> Entity
Day
- day
- entities ->> Entity
So basically, 3 data domains for the years, months and days, months and days being immutable.
That structure is more complex, but it gives a better view of your data. You have a direct access to more information on your data as the data domains are explicit and well defined.
A third solution would be to create a date entity with year, month and day, with one entry per day. A middle ground between the two solutions above. Less interesting I think, but hey, it may suit your needs anyway.

Related

Clustered indexes as PK or PK that is created using FK's (as a serial number)

I'm creating a database that will be used for a web app for editing and preview of a Monthly Work Schedule. The principle behind this is to make a highly flexible layout that will provide building blocks for filtering, preview, and the making of templates.
The way it works:
There are 10 tables - each has a pretty simple data structure (ID, ShortName, Descr).
I want to make a table that will exclusively use data that already exists in those tables and save those combinations as new schedules.
So, you pre-make all the elements and then you put them together like Legos for final product/schedule.
Important - I want to enforce 7 tables- Center, Rank, Role, One, Two, Year, Month. Without all those selected (or if duplicate) you can't save/create a new schedule.
Now, I have two dilemmas here:
1. Should I create clustered indexes from those values like in the image
2. Should I make PK - and make it like serial number so that it's incremented using the data from those tables, e.g. Center(01) + Rank(02) + Role(12) + One(15) + Two(01) + Year(2019) + Month (07) = 0102121501201907
Also, another dilemma is Year/Month entry
I need JUST Year and JUST Month as Columns and values. Which way would be recommended here?
Can I make a Custom Data Type that will trim month and day from date to have just Year?
Should I define Year as int and make a defined table with Months (ID, Name)?
I can't go with a date because I need Year and Month as individual constraining parameters. I don't need Days - and removing day value from a date format through functions and user interface sounds like too much work for something that should be relatively simple. (performance-wise)
Also, making Year and Month as int and defining 12 Months - I'm afraid I'll lose some functionality just because I'm not familiar enough to use it.
This is my first work of this kind, and I feel like these decisions will heavily influence everything I do onward, so any advice whether from experience or from theory/book/site is highly appreciated.

Organizing per-week and daily data in SQL

Problem overview
I'm working on a simple app for reminding the user of weekly goals. Let's say the goal is to do 30 minutes of exercise on specific days of the week.
Sample goal: do exercise on Mon, Wed, Fri.
The app also needs to track past record, i.e. dates when the user did exercise. It could be just dates, e.g.: 2019-09-02, 2019-09-05, 2019-09-11 means the user did exercise on these days and did not on the others (doesn't need to be on "exercise goal" days of the week).
The goal can change in time. Let's say today is 2019-09-11 and the goal for this week ([2019-09-09, 2019-09-15]) is Mon, Wed, Fri but from 2019-08-05 to 2019-09-08 it was Mon, Thu (repeatedly for all these weeks).
I need to store these week-oriented goals and historic exercise of data and be able to retrieve the following:
The goal days for the current week (or any week, let's say I can compute start and end day for any week given a date).
Exercise history for a larger range of days together with goal days for that range (e.g. to show when the user was supposed to exercise and when they actually did in the last month).
Question
How to best store this data in SQL.
This is a little bit academic because I'm working on a small Android app and the data is just for a single user. So there will be little data and I can successfully use any approach, even a very clumsy one will be efficient enough.
However, I'd like to explore the topic and maybe learn a thing or two.
Possible solutions
Here are two approaches that come to my mind.
In both cases I would store exercise history as a table of dates. If there is an entry for that date it means the user did exercise on that day.
It's the goal storage that is interesting.
Approach 1
Store the goals per-week (it's SQLite so dates are stored as strings - all dates are just 'YEAR-MONTH-DAY'):
CREATE TABLE goals (
start_date TEXT,
exercise_days TEXT);
"start_date" is the first day of the week,
"exercise_days" is a comma-separated list of weekdays (let's say numbers 1-7).
So for the example above we might have two rows:
'2019-08-05', '1,4'
'2019-09-09', '1,3,5'
meaning that since 2019-08-05 the goal is Mon, Thu for all weeks until 2019-09-09, when the goal becomes Mon, Wed, Fri. So there is a gap in the data. I wouldn't want to generate data for weeks starting on 2019-08-12, 2019-08-19, 2019-08-26.
With this approach it is easy to work with the data week-wise. The current goal is the one with MAX('start_date'). The goal for a week for a given date is MAX('start_date') WHERE 'start_date' <= :date.
However it gets cumbersome when I want to get data for the last 3 months and show the user their progress.
Or maybe I want to show the user the percentage of actual exercise days to what they set as their goal in a year.
In this case it seems the best approach is to fetch the data separately and merge it in the application (or maybe write some complex queries), processing week by week. This is ok performance-wise because the amount of data is small and I rarely need more than a handful of weeks.
Approach 2
Store goals in such a way that each goal day is a record:
CREATE TABLE goals (
day TEXT,
);
"day" is a day when the user should exercise. So for the week starting 2019-09-09 (Mon, Wed, Fri) we would have:
'2019-09-09'
'2019-09-11'
'2019-09-13'
and for the week starting 2019-08-05 (Mon, Thu) we would have:
'2019-08-05'
'2019-08-09'
but what for the weeks in-between?
If my app could fill all the weeks in-between then it would be easy to merge this data with the exercise history and display days when the user was supposed to exercise and when they actually did. Extracting the goal for any given week would also be easy.
The problem is: this requires the app to generate data for the "gap" weeks even if the user doesn't tweak the goal. This can be implemented as a transaction that is run each time the app process starts. In some cases it could take noticeable time for occasional users of the app (think progress bar for a second).
Maybe there a smart way to generate the data in-between when making a SELECT query?
I don't like the fact that it requires generating data. I do like the fact that I can just join the tables and then process that (e.g. compute how many exercise days there were supposed to be in August and how many days the user did actually exercise and then show them percentage like "you did 85% of your goal" - in fact I can do this without joining the tables).
Also, it seems this approach gives me more flexibility for analysis in the future.
But is there a third way? Or maybe I am overthinking this? :)
(I am asking mostly for the way of organizing the data, there's no need for exact SQL queries)
Perhaps I'm over-thinking this, but if a goal can have multiple components to it, and can change over time I'd have a goal header record, with the ID, name and other data about the goal as a whole, and then a separate table linked with the components of that goal which are time-boxed, for example:
CREATE TABLE goal_days (goal_day_ID INT,
goal_ID INT,
day_ID INT,
target_minutes INT,
start_date TEXT,
end_date TEXT)
I'd have thought that allows you to easily check against the history to map against each day of the goal - e.g. they got 100% of the Mondays, but kept missing Thursday - however when the goal was changed to Friday instead they got better.

Tables in a MVC application

I am learning MVC and SQL and I am trying to make an application that takes daily user data which is put in a day table. The day is linked to a week table and the weeks are linked to a year tables. I have entities for all 3. I am having problems with linking and orgainising them though. When I try link them it always uses their Primary Keys and so I am getting duplicate errors.
For example, when I have it that the user inputs the primary key value, and try to make Week 1 in, say 2015, but there is already a Week 1 in 2014, I get an error because they both have the same ID.
But when I have the computer automatically do the primary keys (so it goes 1, 2, 3, ...), I am only able get the days linked to the primary key. E.g. I have WeekID (the Primary Key) and WeekNo in the model. I have week 1 and 2 made and filled out, then go to make week 3 but accidentally make WeekNo 4, it's WeekID would still be 3 as it is the third week made. I delete it and make a new one and put in 3 as the WeekNo, then the WeekID is 4. This is a problem because then when I make a day, and click the dropdown menu for picking the week it goes in, I get the options "1, 2, 4" rather than "1, 2, 3" because it is using the primary keys. Even if the user never makes a mistake and has to delete it, this is still a problem when the next year comes around and the week numbers repeat.
My question is, does any one know how I could go about making it so that there are no duplicate problems and that they can be linked by something other than the primary key? Basically, when the user makes a week, they can select what year it goes into out of available years from the create page of Week. And the same when making a day, that they can select which week it goes in by number. Has any one encountered this kind of problem and gotten past it?
You are inventing calendar actually.
I would rather have one column with type of TIMESTAMP. And on the server side/ or via SQL queries calculate day/week/year number (you didnt mention which RBDM you use). That will prevent you from having relation issues and also will help you in manipulating TIMEZONES in the future.
Here is the post for JAVA sample where week number is calculated
Otherwise you will need to use composite primary keys for:
day - (DAY_ID, WEEK_ID, YEAR_ID)
week - (WEEK_ID, YEAR_ID)
year - (YEAR_ID)

Standing Orders with SQL / HQL

I implemented an own program to manage my incomes and expenses some years ago. However, I realized that I need some kind of "standing orders" - incomes or expenses which repeat monthly, quarterly or yearly. I would add them in an own table (with the value, description, start and end date, repetition rate, ...). But how do I query them with SQL/HQL in a smart way? For example: I want all incomes for a given month. Now I have to run through all entries and check somehow whether the start date plus a multiple of the repetition rate "hits" the current month. Seems to me very cumbersome. Is there an easy way to implement such operations?
Sorry for answering my own question, but in the meantime I think that it is not that difficult. Even when using HQL it is possible to calculate the number of months between two dates (if not with an existing function, it can still be done in the HQL expression using an obvious formula). Of course one has to handle the case correctly with days which exist in one month but not in the other (e.g., february), but this is a detail which can likely be ignored in my case.
Knowing the number of months between the current month, a simple modulo expression can check whether the current month is "hit" by the standing order. The rest is simple.

NSPredicate with NSDate and repeats (like Calendar app)

I have an entity "Event" witch contains two properties: date (NSDate) and repeat (NSInteger - 0 = NONE, 1 = DAILY, 2 = WEEKLY, 3 = MONTHLY, ...).
Does anyone knows how can I filter events by repeats passing a date ?
Example:
First event: 01-01-2010 / weekly
Second event: 10-02-2010 / monthly
Current date: 10-06-2010
Request:
Get all events where Event.date == "Current date" OR Event.date.day == "Current date".day if Event.repeat == monthly
Returned event:
Second event
I hope someone understand what I'm trying to explain :s
The problem here is that NSDate doesn't have a day (or any other similar) property. If it did, your predicate would work as written.
Data and time programming is deceptively complex under the hood. For example, in common usage, the phrase "same date" means the exact same calendar day. However, from the codes perspective it also means the same week, month and year because days are no more significant to code than any other arbitrary calendar division. Even in ordinary usage, "day" can refer to a specific range of hours e.g. Saturday, August 21 2010 or it can refer to any arbitrary range of 24 hours as in, "within a day." Which one do you need for this app?
NSDate is really an object wrapper around a microsecond accurate timestamp. It has methods for converting to strings and for creating and comparing timestamps but it doesn't understand calendar attributes such seconds, minutes, hours, days, weeks, month, or years. That is what NSCalendar is for and Core Data does not innately support that class as a data type as doing so for all possible calendars would be to complex.
If calendar attributes are required in a model, you need to create an custom entity that models a calendar date. Set the entities attributes to calendar attributes you need to model and then a relationship to the object that needs the calendar date as a property e.g.
CalendarDate{
date:NSDate
minute:int
hour:int
day:int
month:int
year:int
events<--(required,nullify)-->>Event.date
}
You can create a custom class for the entity and provide a method that automatically populates the object based on the passed NSDate and any calendar you choose.
Now your predicate is easy.
NSPredicate *myPred;
myPred=[NSPredicate predicateWithFormat:#"(date.date==%# or date.day==%i) AND repeat=%i", currentDate, 45, kMonthly];
This seems like it is cumbersome but it is required owing to the true complexity of calendar dates. There simply isn't an easy way to handle date and time calculations and comparisons for all uses.
I am not certain this can be done at the SQLite level. It certainly can be done once your events are pulled into memory but that, I suspect, defeats your goal.
There might be some clever way to de-normalize the data and thereby create a situation that can be filtered.
For example, if you had the day of month and month pulled out into integer fields you might be able to devise a way to determine based on those if things align. It is not coming to me directly but I would definitely look in that direction for a solution.
Another alternative, one that calendars tend to use, is to create dependent events for the specific dates coming up. When the event is created you create all of the dependent events so that you are just filtering on a date. Nasty to be sure.
Last option is to pull all events into memory and calculate from there.