Need better database design approach - mysql

I have designed a database schema for a subscription product. user can select subscription starting from a date for certain amount of days. User can cancel a subscription for some days while still keeping the subscription active afterwards. meaning if user subscribes for a month she can cancel it on days say 10,15 and 20 thus paying for only 27 days (30 minus 3).
So far I have come up with this schema.
each user has one profile.
user can select a plan.
once user selects a plan it is noted as transaction which also stores information about start date and duration of that plan.
each transaction has payment (focusing on that part later)
Now, since user can cancel subscription any day how should I keep track of different users and days on which they had subscription?
The solution that I have in mind is crate a new table Plan_Transaction_user which will keep track of each date and transaction ID for that date. This way If user cancels her subscription on particular date there will be no record of that date for that transaction ID.
table will look like this:
Date Transaction ID
1-1-2017 1
1-1-2017 2
1-1-2017 3
1-2-2017 1
1-2-2017 3
Since user associated with transaction_id 2 cancelled for day 2 her transaction record is not present in this table.
Now if I have customer base of say 5000 then in best case within one year I will have 5000 * 365 ~ 1.8m rows. I am sure this is not best approach to go about it. Could you please suggest me any better schema or some changes in existing schema which can be more efficient? Just in case you want to know I will be using MariaDB (AWS RDS) as a database and Python 2 as my language.
Thank you,
Ojas

You can add a end_date field in Transaction table instead of duration. You can easily defined end_date as start_date + how many days you will given for selected plan. When user cancel some days then you can reduce end_date as end_date = end_date - number of cancel days. You can check how many valid subscription currently at any days checking through end_date >= today.

Similar to your Plan_Transaction_user design, if u only need to know how many subscribers for any particular date, but not who they are, u can aggregate the table by day. Like
Date user_count
1-1-2017 1
1-2-2017 2

Related

How to structure MySQL daily checklist database

I am trying to create a database application for daily task. I am converting a paper form I use.
Each row is a task and each column is a date. Every day I go through and complete the task and initial the cell that corresponds with the date. But not every task is required daily. I included an example of how it will appear in the browser.
How do I structure the database?
You could do:
TasksDefinition
Id PK
TaskName NN
TasksWhen
Id PK
TaskId FK, TasksDefinition.id
Day NN --> what day should task Id be completed
History
Id PK
TaskId FK, TasksDefinition.Id
Date NN
Done NN, boolean, default False
PK: primary key
FK: foreign key
NN: not null
Each task is defined in TasksDefinition
TasksWhen stores the information on what day(s) should each task be completed. One entry per task/day of the month (ex. 1 to 31). OR 0-6 if you want to use week days. Using a table allows you to have some tasks completed on many days. Ex. for task X, on day 1, 4, and 28 would require 3 entries in TasksWhen.
At 0001 each day, your application does:
Add each tasks that have to be completed that day to the History table, with the current date and Done == False.
When you have completed the task, change History.Done to True.
When you build your interface, you query the history table only. This will give you which tasks have been done (or not) on each day. The status of completion goes to the History table as well.
You can use day of month or week day to specify which tasks must be done on each day. You could even use a mix of both. As long as your application can figure it out, you would be fine.
The monthly report is built from data in the history table.

Storing expirable credits in database?

It is easy to store user credit with an extra col in the user table, e.g. user_credits, now an extra requirement
for each credit added to the user_credits col, it will auto expire one year after if not being consumed.
Definitely I need an extra table for storing expiring information, e.g.
[table credits_history]
user_id
credits
used
created_at
So, when consuming credits, I need to
check if the user_credits is enough to consume
loop all credits_history table for the user's credit which used = 0 AND now - created_at < 1yr, and set used to 1
update the user_credits
Finally I need to set a daily cron job to update user_credits by looking at the created_at
Are the above approach reasonable? Or any standard way to handle the above requirements?
Don't store things that you can compute unless it's too expensive to calculate. Assuming that the number of rows in credits_history per user is not too high, you can structure your queries in such a way as to check the sum of available credits:
SELECT SUM(credits-consumed) -- see more about "consumed" below
FROM credits_history
WHERE user_id=?
AND created_at >= DATE_SUB(NOW(),INTERVAL 1 YEAR)
A nice side benefit of this approach is that you no longer need a cron job to maintain user_credits.
Consuming credits becomes a little tricky: you need to add a column showing the portion of the credit that has been used. Here is an example to illustrate this point. Let's say a user added these credits in his history:
created_on credits consumed
---------- ------- --------
05/06/2014 50 0
07/12/2014 70 0
12/01/2014 40 0
If you run the query above, you would get a sum of 160.
Now he wants to consume 90 credits. Your code should query all eligible credit records, order them from oldest to newest, and use as much credits as needed to cover the desired amount (i.e. 90)
created_on credits consumed
---------- ------- --------
05/06/2014 50 50
07/12/2014 70 40
12/01/2014 40 0
Now the query above would get a sum of 70.
No I don't think this is a normal approach. Normally you would have a table like user_credits which can have multiple rows for each user, each such row has the REMAINING credit, and expiry date (I think I prefer expiry date rather than created_at, since you calculate it only once). But then you would have a Transactions table which lists credits/debits (positive/negative credits). Buying credits would create a transaction of that amount of credits being added. Using credits would create a transaction of that amount of credits being deducted. Expiring credits would just be another transaction of credits being deducted. Each transaction also updates the remaining credit in user_credits.
The only annoying thing is that to calculate a user's current credits you have to go to user_credits and add up all of that user's rows. Some people might track another value in the main user table to bypass it.
Expiring credits can be done through a cron job to tidy up that table. Although if you want to be stingy you could check the expiry date at the time of the transaction as well to make sure nobody sneaks through just before the cron job.
I would do it as follows
TABLE: USER (id,name,address,phone,etc)
TABLE: CREDIT_TYPE(id,description, expiration_policy, etc)
TABLE: USER_CREDIT(user_id, credit_type_id, activation_date, expiration_date)
A user has zero or many credits
Each credit has some sort of expiration policy, rules, etx
If the CURRENT_DATE > expiration_date, the credit is expired
The expiration date is set immediately based on the policy set forth on the credit_type
Would that work?

Get Daily Active Users for the last 2 weeks

I have a log on our app that logs all user activity along with a unix timestamp and I now want to create a function that will return the data needed to display the number of DAU each day in the last 2 weeks. Ive googled around a bit , but I have been unable to find a straightforward question on Stack regarding how to even start to go about such a query . Maybe my querying knowledge just is not advanced enough , excuse my ignorance if this is the case. I just have no idea how to group individual dates + unique user totals in a single query.
my table is setup as following
|LOG_ID | TYPE_OF_REQUEST | USER_ID | TSTAMP |
You can start by filtering
WHERE TSTAMP <= DATE_SUB(NOW(), INTERVAL 2 WEEK))
To filter only the two last weeks
And then
GROUP BY USER_ID
Perhaps combining a COUNT(TYPE_OF_REQUEST)
Depends on what info you are trying to achieve

Mysql performance selecting blog post views by date

Question about SQL performance when selecting a 'blog post' based on user views by date.
I want to record the user views of each post, and i ll select everyone of them using 'daily' and 'monthly' as parameters:
PS:
Most viewed posts of the day, or month.
To record the views, i created a table to insert, after every page load, the date of each view.
And them select them (count them) by DAY() and MONTH() when needed.
The problem here is, when the table or the amount of users requiring this information grows the select starts to be slower, due to the amount of rows(views) multiplied for the amount of posts.
One alternative that i thought was, create a table for daily records, and another table for monthly records, then on every page load the code checks if there is a row for the selected date, if the rows exist the script increment the views count on it, if it doesn't, the script insert the row with views count = 1;
Ps:
Daily Views
Post ID | Views | Date
1 | 898 | 2014-07-11
2 | 676 | 2014-07-11
1 | 333 | 2014-07-10
This way every post can have only one row per day.
Is there any better option? what do you think about my alternative? there is no need for my suggestion?
I think the best solution is:
Create a table with statistical data with fields:
id
date (store date m-d-y)
day
month
year
views (store number of visits)
page (store blog post)
One unique row per day, and update programmatically as needed.
Then you can make queries using day, month, year fields, even you can add weeknum field to make queries to obtain statistics grouped by weeks.
As addition you can add a second table to store the full date (m-d-y h:m:s) for each visit, you can add fields like browser, ip, etc... to this table.

SQL: Designing tables for storing agendas/schedule

I'm trying to create a weekly (Monday - Sunday) schedule/agenda, similar to Google Calendar, using mySQL where users are able to fill out and display a schedule for what tasks they have at some day during some hour interval. For example a user task could store in the schedule as
Username | Day | Time | Task
Jimbob, Tuesday, 13:00, Eat super delicious spaghetti.
I'm wondering what is the best way to design the tables?
I could create a table for every day of the week, or have one big table that will store info for any day of the week. But what if I have a million users, would one big table be a performance issue?
Also, for the field of the tables I was planning to make one row store only one task for each hour, but I could also store all the tasks for each hour of the day. The latter could result in a lot of null values and take up more memory, but if the users fill out a lot of the calendar it seems like it could reduce a lot of rows, and redundant username entries. It also makes it easier to print out the schedule. Any thoughts?
Username | Hour | Task|
or
Username | 12am |Task1 | 1am | Task2 | 2am ....
Thanks.
The best way to do this, imo, would be to have two tables: one for users and one for tasks.
Users would have a user ID, a username plus whatever else.
Tasks would have the date and time, the description of the task, a task ID and the user ID of the user whose task it is. You shouldn't make a table storing each day of the week since you can just query the dates of the tasks. This is how you should design the tables. The actual calendar and the queries should be done in PHP or whatever you want to use for it.