Badge System with Variant Criteria, DB Design - php

Working on the DB design of a Badge System which will work synchronously(request/response) for variant criteria for more than 1000+ badges I am wondering how the badges will be checked when a user does a request.
A first DB Schema I am thinking is:
Badge
id name
1 Check-in 10 Beers, of X Manufacturer the last 30 days
2 Check-in 5 Organic Beers from England
3 Comment on 5 different check-ins
Rules
id key
1 count_of
2 manufacturer_of
3 last_x_days
4 comment_x_diff_checkins
5 from_country_x
BadgeRules
badge_id rule_id value
1 1 10 (check-in 10 beers)
1 2 122 (manufacturer_id)
1 3 30 (days)
The think is that the User makes a check-in and the system should check the different variants for different badges.
As an example need to check on the above DB data that the User made 10 check-ins of Manufacturer with ID 122 the last 30 days. Obviously makes no sense to check 1000+ badges for each request but somehow can be checked only the Badges that the User can win for that check-in.
I was thinking to save for user's state in a different db table but I dont think that this can work for all the rules like last_x_days
UserState
id user_id key value
1 1 checkins 8
2 1 comments 12
P.S. I have seen the Untappd app which has thousands of Badges and it assign them on users request.
I would be grateful for a proposal.

Related

Can 4 fields be put together to use as a key to join two tables?

I am trying to create a solution which allows pilots to swap a 1, 2, 3 or 4 day trip for another pilot's 1, 2, 3 or 4 day trip. I have 3 tables, Pilot, Have and Want. A pilot creates a Have which, for example, is a 2 day on 10/20 (October 20th). This pilot wants a 3 day starting on the 22. Another pilot has the 3 day and he wants something like the other pilot's 2 day. The tables looks like this;
id_pilot, name, phone, employee_num, aircraft, base, seat
1 Steve 363-0040 123454 320 DCA FO
2 Ted 992-5380 123455 320 DCA FO
id_have, id_pilot, daytrip, start_month, start_day
1 1 02 10 20
2 2 03 10 22
id_want, id_have, daytrip, start_month, start_day
1 1 03 10 22
To see what Wants are out there for a particular Have I need to join the Want and Have table on something that looks like DCA|320|FO|10|20|2. I only want to see the Wants for a particular Have that are for the aircraft, base and seat. I can do this by creating a new join field but having such a simplistic understanding of MySQL I imagine there is a way to do this on the fly. I used joins to grab information via the primary keys but this seems like it's one step removed from that. What would such a query look like?
To give an example of what Marc B is saying, the join clause can compare the daytrip, start_month and start_date columns between the have and want tables, e.g.
select have.id_pilot, want.id_pilot, want.daytrip, want.start_month, want.start_day
from have
inner join want
on have.daytrip = want.daytrip
and have.start_month = want.start_month
and have.start_day = want.start_day
The 'on' clause is executed on each row comparison between the two tables and the only absolute is that it must return true or false. So any column from either table can be used in the evaluation in any combination.

What is best way of changing between active and upcoming_active records?

I'm using tokens for how many messages a user can send (1 message requires 1 token). At the moment I've just got it subtracting the value from an overall value to check if the user has tokens remaining and that's working fine.
I'm trying to change it so that it shows which bundle is active, so I need to check if the user doesn't have enough tokens remaining in the active bundle change to the upcoming_bundle.
Example:
Stored User Data:
Table Name: Tokens
First Record
id: 1
user_id: 5
bundle_type: small
value: 10
value_remaining: 4
state: active_bundle
Second Record
id: 2
user_id: 5
bundle_type: large
value: 100
value_remaining: 100
state: Upcoming_bundle
User sends 10 messages (10 tokens)
Only 4 remaining tokens in first record. Use 4 remaining tokens and leave
6 tokens
Then subtract the 6 tokens from second record which is now active so that will leave 94 remaining tokens.
Should I have a check to database every time the message is sent and update the database to subtract 1 token at a time, then when the remaining_value hits 0 change active_bundle to inactive and upcoming_bundle to active?
If this is your data model then I would fetch all active & upcoming bundles and then do the logic in php, e.g. subtract remaining tokens, change status, etc and then update them as a transaction.
If you are flexible on how the data is structured, I would rather have some kind of transaction log, from which I can read each action, i.e. whether a bundle was added or a token was used with a timestamp. For example like this:
id | user | change | comment | timestamp
1 | 1 | 10 | bought small bundle | 2016-09-06 09:30:00
2 | 1 | -1 | sent message | 2016-09-06 10:56:00
3 | 2 | -3 | sent multi-message | 2016-09-06 10:57:00
Where id is the transaction id, user the user id, change is the number of tokens added (by adding a bundle) or used (by sending one or many messages) and comment a message describing the action. When you want to find out how many tokens there are left you can just do a search for that user and check their SUM(change) instead of weird searches for active/upcoming bundles. Obviously this can be more or less elaborate depending on your needs.
This does not take into account your actual domain! There are more approaches each having their drawbacks. For example my approach might have problems wen the transaction_log-table gets large because of number of users and increased activity, although it is very unlikely (I have seen mysql perform well with a few million records in a similar log table). The important part is: You should figure out what is important to your use case and build a solution around the requirements.
What I would do is, I would subtract it one at a time, not only this is safer, but also a lot easier.

Sum user working time in a given MySQL table

The following table has the time when the user enters/quit on the company:
ID | user_id | day_unix | time
10 1 1459220400 1459293745
9 1 1459220400 1459293711
8 1 1459220400 1459293689
7 1 1459220400 1459293678
6 1 1459220400 1459293669
11 1 1459220400 1459293761
day_unix consists in the first second of the day, this way, GROUP BY can be easily used in future.
time consists in the time that the user click's on the button to start/stop working. Time also can be changed to a native DATETIME column.
I want to create a SQL query capable of summing the time between the entries. So the query must to jump "odd" entries that can be considered as the beginnig of a small coffee break and right after the "odd" entries the user has started working again, so the query must to sum the working time, excluding the coffee breaks.
Any idea? Here is what I've already got:
SELECT * FROM table GROUP BY day_unix ORDER BY time ASC

`Retrieve a page based on a predefined path using nested set

I am currently building a CMS and intend to use Joe Celko's nest tree set (MPTT) to retrieve hierarchical data such as a nested deep page based on a defined path. e.g.
example.com/products/phone/specs.
I have a categories tables
cat_id name lft rgt
1 products 1 18
2 phone 2 9
3 specs 3 4
4 gallery 5 6
5 price 7 8
6 laptop 10 17
7 specs 11 12
8 gallery 13 14
9 price 15 16
10
and a pages table
page_id name content
1 specs Here is the specification of the chosen phone
2 price the price of chosen phone
3 specs laptop spec
4 price laptop price
This schema can be found on slqfiddle
Unfortunately, i have no idea on how to do this, the closest idea i got from searching online examples even celko's book does assume you only want to "retrieve the single path" to the parent of a node. Whereas, my use case demands that i retrieve the page (and display it) based on the path set by the url as above.
So, i can i achieve this? All valuable suggestions are welcome. thanks.

MySQL select records that sums

I am using MySQL and PHP. I have a table that contains the columns id and quantity. I would like to retrieve the id of the row that is the last to sum quantity as it reaches the number 40 at sum. To be more specific. I have 3 rows in the database. One with quantity 10, one with quantity 30 and one with quantity 20. So if I sum the quantities to have the result 40, I would sum up the first two witch means: 10 + 30 = 40. That way, the last Id that is used to sum the number 40 is 2. I just want to know the id of the last row that is used to complete the sum of 40.
I would give further details if asked. THANK YOU!!
Let me put it this way:
I really have 6 products in my hand. The first one came to my possession on the date of 10, the next 3 came on the date of 11 and the last 2 came on 12.
Now, I want to sell 3 products from my stock. And want to sell them in the order that they came. So for the customer that wants 3 products I would sell him the product that came on 10 and 2 products from the ones that came on 11.
For the next customer that wants 2 products, I would sell him one product from the date of 11 that remains from the last order of 3 products, and another one from the ones on 12.
The question is how would I know which price had each product I sold ? I thought that if I can find out which rows sums up every requested quantity, I would know where to start the sum every time I want to deliver an order. So first I would look which rows sums up 3 products and keep the entry id. For the next order I would start the count from that ID and sum until it sums up the second order of 2 products and so on. I thought that this way, I can keep track of the incoming prices that each product had. So I won't sell the products from the date of 12 at a price made up using the first prices.
I hope you understand. I just need to know what price had any of my products so I would know that the first products would have one price but as the product prices raises, I must raise my prices too...So the last products that came must be sold for a higher price. I can only achieve that if I keep track of this...
Thank you very much.
Nobody ? Or, even easier: MySQL should select the needed rows for SUM(quantity) to be higher or equal with 40 for example. And then to get me the id of the last row that participated at the sum process.
Have a third column with a running total. Then you can simply return the last row where the running total <= your target value.
So your table should look like:
ID Quantity RunningTotal
1 10 10
2 30 40
3 20 60
NOTE: If you delete a row in the table, remember to update all subsequent rows RunningTotal -= DeletedRow.Quantity!
I don't understand your question too well. Can you try rewording it more properly? From what I interpret, here's the structure of your database:
ProductID ArrivalDate
1 10
2 11
3 11
4 11
5 12
6 12
Now you are asking, "how would I know which price had each product I sold"? Which sorta confuses me, since each value in the database has no price attribute. Shouldn't your database look like this:
ProductID ArrivalDate Price
1 10 100
2 11 200
3 11 300
4 11 300
5 12 400
6 12 400
Personally, I think your idea to find out price sold is flawed. It would make more sense to add a few more fields to your database:
ProductID ArrivalDate Price InStock DateSold
1 10 100 Yes 17
2 11 200 Yes 17
3 11 300 Yes 18
4 11 300 Yes 18
5 12 400 no
6 12 400 no
In changing your database, you can easily keep track of when a product arrives, the date sold, its price, maybe quantity (I can't tell if its an actual field or not).
Furthermore, you can simplify and make your life easier by separating the sql queries, or even adding some code to do some of the work for you.
Relying on table ID's is probably a bad idea for this, but if that is how it is really done, you could try something like this (not tested):
SELECT yourTableA.id
FROM yourTable AS yourTableA
JOIN yourTable AS yourTableB
WHERE ( yourTableA.value + yourTableB.value ) = 40
AND yourTableA.id != yourTableB.id
ORDER BY yourTableA.id
This type of solution will only work if your expecting that you only need two rows ever to equal your target sum. Since this is most likely not the case, your best bet is probably to try and get all of the rows and do this programaticly on the returned data.
The Running Total solution posted by lc is also a good option although I generally try to avoid storing calculated data unless I absolutely have to.
Based on the updated information from this request, I have an alternate answer.
It doesn't sound so much like you care about the inventory. You care more about when the products came in.
SELECT *
FROM product
ORDER BY product.receivedData
Process each record as they come in, store the price for that record, and keep going for as long as you need to until you reach the number of items you need. You should end up with a list of items, the number of inventory at that level and the price at that level.

Categories