Good way to map table based on members and months in year - php

I need to make simple table like this
This table represents yearly membership status for each month, it's super simple there is no advanced stuff there, that membership status is updated manually by admin (when member pays, admin update status for that month).
From database standpoint i have 2 simple tables like this:
As you can see i have membership table with member_id and date, and when payment is made that table is being populated.
So my question is: what is good way to map data to match that table ?
i'm thinking something like this (for example i want all members data from 2017 year)
select m.*,
GROUP_CONCAT(MONTH(ms.date)) as dates
from members m
left join membership ms on m.id = ms.member_id
where ms.date >= 01-01-2018
and ms.date <= 12-31-2018
this query should return list of members with dates array(array of numbers, each number represents month), so on UI side i could maybe generate this table view based on that ? Any suggestions for this, anyone have better solution ?

Your query seems fine. You can then use it to determine whether or not a month exists in the array of dates by using the includes function on the date array.
For example, to check if there should be an X for February you would check dates.includes(2). If true, place an X, otherwise leave blank.

Related

How to sort a MySQL query by a mathematical relation between multiple columns in multiple tables

I am developing a MySQL DB for an online jewelry shopping website. Every jewelry may have different stones, metals, … Their net prices will change everyday (e.g., every ounce of gold may be X today and Y tomorrow). Product A may be more expensive than B today, but cheaper tomorrow. Every time a user requests the products list, the website automatically calculates the price of every product.
How can users sort products by its price, considering that net price is not stored in a field in DB and every time will be calculated (using weights, net fees, …) by a function named func($id), where $id is the id of the product.
An illustration to the question:
Assuming you have all of your data in your DB, there is no reason you can't ORDER BY any kind of expression you want.
If, say, you had a table products (pid, name), productContents (pid, type, amount), and contentPrices (type, price), you could run something like
SELECT a.name, SUM(b.amount*c.price) AS totalPrice FROM products a
LEFT JOIN productContent b ON a.pid = b.pid
LEFT JOIN contentPrices c ON b.type=c.type
ORDER BY totalPrice DESC;
EDIT to work with the updated question schema:
SELECT p.title, p.metal_weight*m.fee + p.stone_weight*s.fee AS totalPrice FROM Products p
LEFT JOIN Metals m ON p.metal_id=m.id
LEFT JOIN Stones s ON p.stone_id=s.id
ORDER BY totalPrice DESC;
I would do it in two ways, but first of all, if you want a good performance, store the price value in a Field. I don't think it changes every second, does it?
So first, if you can make your Price calculator function in MySQL, create a Stored Procedure for it and run this on the table periodically (for example every day at 00:00) and store the value in a field. You can find how to schedule an event in MySQL here.
If you can't do this, stay with php, but it also should called periodically to calculate everything. You can do it with cron on Unix (if you have the rights). This can be scheduled on a remote server too, so if you have somewhere a server where you can use cron, simply call the updater.php with wget, for example:
wget -c http://jewerly-shop.com/update_prices.php && rm -rf update_prices.php
But if you need further help, we need more info (like the function that does the calculating and the table structures).

Need to get all records from DB where ID is not known

in my project, the user can enter categories into a budget. So the user enters an annual amount in a form field. They can then select "monthly" or "annual" from a dropdown menu, and also select which month they want the annual category to start on. So if they select annual and June, then records would be stored into the categories table, one for each month (June, July, August, etc.).
Here's my problem. I want to be able to get a running sum total of the amounts in a particular category to display to the user on the page, but when an annual category is stored to the database, its given a unique id as the primary key, and that's obsiously auto-incremented.
So where I would normally select records where the ID = x, I can't do that because the ID is not the same.
The only way around it that I can think of is to get them by category name since they would all be the same. Is that even ok to do? There's nothing to stop the user from going in and creating another category with the exact same name, since the name is not unique.
You've already answered your question. If the user creates two categories with the same name the page will appear broken.
Try to normalize your schema, it will probably help you.
Something along the lines of
budget_entry_per_year(category, amount, starting_month)
budget_entry_per_month(category, amount)
You can add IDs that are unique to both tables if needed.

Comparing two mysql db tables/arrays in php and updating based on timestamp

Horrible title I know, I couldn't find a better way to articulate my problem.
I searched through SO without success, but if the answer's already out there, feel free to link it and I will read it thoroughly.
I'm building a book ordering system. People will order books throughout the month, and one large order arrives at the end of every month.
Orders get recorded into an Order table with the following fields:
order_id, book_id, quantity, language, person_ordering, timestamp, month, year
When orders arrive, they are inputted into an Received table with the following fields:
book_id, quantity, language
Now suppose one person orders (2) copies of book 1. And another person orders (3). And another (5). For a grand total of (10).
Then the order arrives and it only has 7 copies.
I'm trying to write a script/function that will find out:
Which persons will receive their copies of the book (it's first come first serve, so the people that ordered it first will have their order fulfilled first.
If a person can only have their order partially fulfilled, to update the Order table (or possible a new Pending Orders table is needed?) to reflect that they have X amount still waiting to be fulfilled. Then the following month, their orders would be fulfilled first, again, based on date ordered.
I thought about pulling the information from the Orders table based on time-stamp of when the order was made and sorting through it, then pulling the information out of the Received table and somehow comparing the two arrays and updating a third array?
Or perhaps there's a simpler solution that I'm missing.
If more information is needed, I will gladly provide.
I've been pulling my hair out over this problem for 2 days straight.
Any help would be appreciated.
Thanks!
To me, it sounds like you could do away with your timestamp, month, and year variables to be replace with a single datetime stamp.
Also, add a column in your Received table for the datetime stamp of collection.
Finally, make another table to keep track of fulfilled orders.
Do a select * on the month needed ordered by the datetime stamp descending for your "orders" data.
In a separate query get your "received" data for programmatic comparison using the correct month.
Put the "received" quantity of each "received" book_id into separate variables (probably a key/value array).
For each "received" book_id, compare and compute the "ordered" quantity needed from each order in a sub-loop, updating the "received" quantity value. At the end of each iteration of your sub-loop, if your "ordered" quantity does not equal 0, then you need to add another entry for the next month with the remaining quantity, also enter all fulfilled orders into your new table.
* You **will not** want to modify any existing records, as you will almost certainly need this untouched in the future.
Clear as mud? :P
* Database tip - Every entry of every record should **always** have a datetime stamp and when possible, a "who did it".
You can try with adding an extra field in Received table.
when the order is coming you can calculate the number of copies that we can provide at that time.
book_id, quantity,qty_available, language
1. B1 2 2 EN
2. B1 3 3 EN
3. B1 5 2 EN
Assuming that the Received table is for current stock, and that orders are only shipped when the entire order can be shipped, here's a suggestion for what you can do:
Add a field to Order for whether the Order has been fulfilled.
Select from Order who have ordered the book, ordered by date. You can limit by quantity of Received.
Start fulfilling the Orders and marking them as such until quantity from Received is reached.
If what is left of Received quantity is not enough to fulfill an Order, leave it and leave the remaining quantity in Received for next time.
Hope it makes sense.

Most viewed posts in the last 21 days

I'm trying to create a blog system with php and I need a way of counting the most viewed post during the last 21 days. Does anybody know of a good way of doing this?
I don't have much experience with php so i need someone to point me in the right direction. I have tried to look into google anlytics API but it seems a bit complicated. Would it be easier to just use cookies?
Since you are building your own blog system, here is the simplest way to do it:
I assume that this is anonymous counting of your blog post visits. If you want to have ip logged records you'll have to adjust the business logic.
Create a new table called visits.
Add an id (Primary key), a field called blogpostid ( that will store the id of the post being viewed) and a field called dtpost with timestamp or datetime properties that upon insert will automatically put the date/time.
Now you can query like this:
select visits.blogpostid, count(dtpost) as counted from posts
left join posts on posts.id = visits.blogpostid
where dtpost between (NOW() AND <-21 days interval function>)
order by counted DESC group by visits.blogpostid;
What you really do is storing the datetime of someone visiting your blog post. This automatically is a count so whatever interval you put the between will fetch the data *. Then the count() function does the counting.
One thing to know is that your own browser refresh will add up to the counting of the visits so you'll have to provide a way to block counting the refresh of the browser (usually a time limit or a cookie to say that you have already seen that page).
*Edit:
Since this is ambiguous, what I mean is that it will fetch your data within the time period that you want.
Create a table views with a foreign key to post ids and add an entry with the visitors information and the date. Then you can fetch the most viewed posts like this (untested):
SELECT p.*, COUNT(p.*) count FROM posts p
INNER JOIN views v ON v.post_id = p.id
WHERE DATE_SUB(CURDATE(), INTERVAL 21 DAY) <= p.date_viewed
ORDER BY count DESC
GROUP BY p.id;
To prevent the count from incrementing when viewed multiple times by the same user you can use the session meganism to prevent that from happening.
session_start();
if (!isset($_SESSION['posts_viewed'])) {
$_SESSION['posts_viewed'] = array();
}
// some logic to get to relevant post id here
// check that the post_id is not in the array
if (!in_array($post_id, $_SESSION['posts_viewed'])) {
// logic to increment a persistent counter (e.g. in mysql) here
// add post_id to array
array_push($_SESSION['posts_viewed'], $post_id);
}
// finally some logic that display the post here
session_close();

User's possibilities on site

I want to build a system on the website, that allows users to do some things depend on their rating. For example I have rule for rating value X:
1 post in 3 days
10 comments in 1 day
20 votes in 2 days
for rating value Y, rule may be following:
3 post in 1 day
50 comments in 1 day
30 votes in 1 day
Each night I recalculate users' ratings, so I know what each user is able to do.
Possibilities don't sum or reset on each rating's recalculation.
One more important thing is that admin can fill concrete user's possibilities at any time.
What is optimal database (MySQL) structure for desired?
I can count what concrete user has done:
SELECT COUNT(*) FROM posts WHERE UserID=XXX AND DateOfPost >= 'YYY'
SELECT COUNT(*) FROM comments WHERE UserID=XXX AND CommentOfPost >= 'YYY'
But how can I do admin filling possibilities in this case?
I would log the number of actions of each user each day and use that table to compare.
This table would contain the following fields:
date: the day when the action took place
count: the number of actions took that day
userId: who did this action
action: which action post/comment/vote/...
ignore: boolean, if this is set, admin has reset the values
Checking a rule: SELECT SUM(count) FROM log WHERE userId = XXX AND action = YYY AND ignore = 0 AND DATEDIFF(date, NOW()) <= DAYS
Resetting a rule: UPDATE ignore = 1 FROM log WHERE userId = XXX
If his rating changes the result is still valid (you'll just compare with on other total)
When you create a rules table:
action
limits
days
rating_min
rating_max
You can query for permissions like this:
SELECT action, IF(SUM(count) < MIN(limits), 1, 0) as can_do_action FROM log LEFT JOIN rules ON rules.action = log.action WHERE userId = XXX AND rating_min <= RATING AND rating_max >= RATING AND ignore = 0 AND DATEDIFF(date, NOW()) <= days
So you get a table loggin like this:
- comment => 1
- votes => 0
You do have to update this table every action (create a new row if first action of the day or update the count of the row)
The absence of a rule means no actions have been made, so we can ignore it.
If I understand you correctly you have a user that can post 1 blog, and comment 10 times. Now he/she has commented 5 times and posted a blog. You want the admin to click a button, and now the user can again post a blog and comment 10 times?
It might be a bit of a hack, but you could count the actions that are being reset/ignored, and substract that from the current actions?
e.g.: user has 1 blog and 5 comments. Admin presses "reset", and you save those values.
Now as the user posts another blog, and you check if that's allowed, you'll get
SELECT COUNT(*) FROM posts WHERE UserID=XXX AND DateOfPost >= 'YYY'
And you do something like this
SELECT changes FROM adminTable WHERE UserID=XXX AND type = 'post'
And if count - changes is ok, you're set.
What about having, in the user table, three columns called remainingPosts, remainingComments and remainingVotes? You'll decrease the columns when the user has performed a specific action, and in this way the admin can always "refill" those columns, even above the original limit.
===
Another option is to store the expiration of the permissions in those columns, so that you can reset the permissions just putting the expiry for a certain column to the day before. You can then use your queries to get the remaining number of posts/comments/votes for the user.
I suggest separating the two concerns entirely:
The process of enabling features/possibilities to users
The data model of user features
For example, you could have a simple many-to-many table representing user features:
user_features(
user_id
,feature_id
,source (admin|earned)
,primary key(user_id, feature_id)
);
This makes it really easy for an administrator to disable/enable parts or all of the feature set.
Your nightly job would query relevant tables and grant/revoke features by inserting/deleting from this table.
If you go with this approach, you can actually give the features either based on a rating or specific actions.
A rule such as "3 posts in 3 days", can be implemented like this:
when a user posts, check if the previous post was made within 24 hours.
if yes then
increment counter by 1
record current timestamp
if counter = 3 then
grant feature to user
else
reset counter to 1
record current timestamp
You would need two columns (post_count:int, last_post:date) in some table keyed by user_id.

Categories