Cheapest way of managing relational SQL data [closed] - php

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I'm trying to find the best method for managing the huge-relational game data.
Let me explain my data structure.
There are three main data field. User, Bets and Coupons.
+----------------------------------------------------+
| bets |
+----------------------------------------------------+
| id | status | yes | no |
+----+-----------+-----------------+-----------------+
| 1 | 0 | 1.45 | 2.52 |
+----+-----------+-----------------+-----------------+
| 2 | 1 | 3.00 | 1.08 |
+----+-----------+-----------------+-----------------+
| 3 | 2 | 2.43 | 1.42 |
+----+-----------+-----------------+-----------------+
+----------------------------------------------------+
| coupons |
+----------------------------------------------------+
| id | played_by | bets | status |
+----+-----------+-----------------+-----------------+
| 1 | 1 |1,yes;2,no;3,yes;| 0 |
+----+-----------+-----------------+-----------------+
| 2 | 2 |2,yes;3,no;1,no; | 0 |
+----+-----------+-----------------+-----------------+
| 3 | 3 |1,yes;2,no; | 0 |
+----+-----------+-----------------+-----------------+
Information: Every bet has yes/no choice. Users play bets. We register them inside of coupons. If all bets inside a coupon WIN, coupon wins and user get extra balance. Classic. Please note that there will be so many bets (avg. 5 per coupon), so many coupons played by users (thousands), and thousands of users.
So I'm trying to find best method for finalizing bets and checking coupons for win or lose process.
Method 1 I tried;
We finalized Bet ID: 2 as yes;
Check 2,yes; with "LIKE" operator in coupon, if there is, concat(append) 1 to progress field.
Check how many bets are there inside the coupon.
If count of 1s equals to numbers of bet inside this coupon, set coupon status to WON.
Method 2 I tried;
Finalize bets; YES or NO
Check related coupons with a cron task.
I liked both methods, but I want users see their progress immediately, so I am not sure about the cron method. Both methods work fine, but I have doubts what will happen when there are thousands of users.
I hope I described my issue understandable. I'm looking for comments and suggestions.
Thanks.

Instead of appending a user's bet to a value in a coupon (which is highly inefficient since you're having to use the LIKE operator), it makes more sense to just create a table of coupons that store the ID of the bet its associated with it, the ID of the user it belongs to, and the value of the coupon (YES or NO). So your Coupon table would look like the following:
Coupons
ID BetID UserID Value
1 1 10 YES
2 1 11 NO
Now if you want to acquire all of the coupons associated with Bet #1, you would just do a SELECT * FROM coupons WHERE BetID=1.
If Bet #1 wins, all you would need to do is acquire the value of the bet for the winning choice, and update all of the users who fall under the choice. For example:
# Select the winning value:
SELECT <winning value>
FROM bets
WHERE id = <id of completed bet>;
# Update the users:
UPDATE users
SET balance = balance + <winning value>
WHERE id EXISTS (SELECT userID from coupons where betID = <id of completed bet> AND value='<winning value>');

Related

How to save, handle the order total amount in an orders, ordersDetails schema?

When I started designing my application database schema few months ago I have been told not to store the same data/calculated data in more than one place in the database(normalization). If I do, I will make a scope of bugs when I update the data in one place and left the other without updating. So I did an orders table and ordersDetails table. Something like this..
-- orders table
+-----+---------+----------+
| ID | clintID | date |
+-----+---------+----------+
| 1 | 1 |2018-02-22|
| 2 | 1 |2018-02-23|
| 3 | 2 |2018-02-24|
+-----+---------+----------+
-- orderDetail table
+-----+---------+------------+----------+----------+
| ID | orderID | itemNumber | quantity | unitPrice|
+-----+---------+------------+----------+----------+
| 1 | 1 | 12345 | 3 | 100.75 |
| 2 | 1 | 12346 | 3 | 100.75 |
| 3 | 2 | 12347 | 3 | 100.75 |
| 4 | 2 | 12345 | 3 | 100.75 |
| 5 | 3 | 12347 | 3 | 100.75 |
| 6 | 3 | 12345 | 3 | 100.75 |
+-----+---------+------------+----------+----------+
And to make the the queries easier for me I made a view "allOrdersSummary" like
-- allOrdersSummary
SELECT
orders.*, SUM(orderDetail.quantity * orderDetail.unitPrice) totalAmount
FROM orders INNER JOIN orderDetail ON orders.ID = orderDetail.orderID
GROUP BY orders.ID;
and I used this view later for my queries, but now I started to get the MAX_JOIN_SIZE error.
So I thought of saving the calculated total order amount along with the orders table ID, clintID, date, totalAmount and whenever I change something in the orderDeatils table I update the calculated totalAmount column in the orders table, I don't know if this is good or bad!
This problem -I don't know if this is considered a problem or not- is encountered many times, for example to know the unread messages of the client making the request I have to do sum(messages) unread from messages where to = ? and isRead = 0
A) should I make another column for calculated totalAmount in the orders table or it is a normal thing in databases to calculate the totalAmount from the orderDetails table every time I need it ?
B) If you recommend making another column in the orders table, what is the best way to update it every time a change happens in the orderDetails table ? should I update it at the PHP layer whenever I update the orderDetails table, or this is something that needs a stored procedure ?
Yes, it is normal to store pre-calculated values, based on other data in the database, in a database. But not necessarily for the reason you mention. I never had a problem with MAX_JOIN_SIZE.
The main, and probably only, reason for storing calculated values is speed. So you do it for values that don't change that often and that may be used in queries that use a lot of data and may therefore be too slow if you didn't use them.
For instance: If you want to know the average value of all the orders in your database the query would be a lot faster if you already have the order totals.
Why, and how, you update the values is completely up to you. However you have got to be consistent about it. If you use the MVC pattern it would make sense to integrate it in the controller. Or in simple terms: Whenever a form is submitted that could change one of the values, out of which the pre-calculated value is computed, you need to recompute it.
This is a clear demonstration where 'normalization' is not entirely maintained. It's not really pretty, but sometimes worth it. You could, of course, argue, that the calculated value represents 'new' information, and therefore does not offend against 'normalization'.
You have an "inflate-deflate" problem.
JOIN the two tables to make a much larger temporary table.
GROUP BY to shrink back to one row per row of the original (orders) table.
This avoids the problem:
SELECT *,
( SELECT SUM(quantity * unitPrice
FROM orderDetail WHERE orderID = orders.ID
) AS totalAmount
FROM orders;
Please let me know how your experience is with this one. It is one of the simplest examples of the inflate-deflate problem.

Mysql - select from multiple tables without producing duplicate data [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have three tables and I would like to select from the tables without producing duplicates.
The table are as follows:
Customers
id | name | lastName
---------------------------------------
1 | john | doe
2 | helen | keller
Orders
The userID column is a foreign key that references John Doe, so John orders 3 items.
id | userID | order
---------------------------------------
1 | 1 | pizza
2 | 1 | pasta
3 | 1 | lasagna
CustomerRating
The userID column is a foreign key that references John Doe, so John leaves 5 reviews.
id | userID | rating | comment
-------------------------------------------------
1 | 1 | 5/5 | was good
2 | 1 | 5/5 | excellent
3 | 1 | 4/5 | great
4 | 1 | 4/5 | great
5 | 1 | 4/5 | great
How would I select from the 3 tables where I can get a return results that look like this?
id | name | lastName | order | rating
-----------------------------------------------------------------
1 | john | doe | pasta | 5/5
| | | pizza | 5/5
| | | lasagna | 4/5
| | | | 4/5
| | | | 4/5
I've tried joining these tables, but since John has left 5 reviews and only ordered 3 times, the id, name,lastName, and order columns gets filled with duplicate data.
Thanks!
I don't have any experience in MySQL but I assume that it works similar to MSSQL.
So the format in which you are expecting the output is not possible. You can rather get the order and rating column values as comma separated
Here is a similar kind of question that might help you
including example based on link
try something like this
SELECT Customers.id, Customers.name, Customers.lastName,
GROUP_CONCAT(Orders.order) OrderedDishes,
GROUP_CONCAT(CustomerRating.rating) RatingsGiven
FROM
..... rest of your query .....
There are ways to discard duplicates (SELECT DISTINCT, UNION, GROUP BY) but it is not clear whether users update existing rating or create new ones. And what you want to see: the last rating or the average one
On the other note - i would change your entire setup:
order table would contain order_id, customer_idand other order related stuff like order_date
products table that would describe each of your dishes and their info like price, description etc
order_products table with fields order_id and prduct_id
if users rate products then your rating table would need at least product_id, customer_id, rate_value. I'd also add ratingDate That way you can get averages or select the last one by Max(ratingDate)
I think you need to add an orderID field to the CustomerRating table else there is no way to relate an item to its rating.

Finding blank cells in a specific row with php/mySQL

I am attempting at making a site that has a polling section in it. The users need to be able to take previous polls if they have not voted in them yet. So for each poll they take, it adds a note into the cell.
The table looks something like this:
userid | poll1 | poll2 | poll3 | poll4 /
---------+--------+--------+-------+--------/
001 | Y | Y | | /
---------+--------+--------+-------+--------/
002 | Y | | Y | /
--------------------------------------------/
So now, the code would have to select poll 3 and 4 for user 001, and present that to the user on the page. I have been trying a couple different approaches to this but can't seem to find the right code.
I have looked for something for help online and haven't found anything that addresses this issue.
Users
id | name
---+-------
1 | tyrion
2 | cersei
Polls
id | name
---+-------
1 | first poll
2 | second poll
UserPolls
user_id | poll_id
--------+-------
1 | 1
1 | 2
2 | 2
In the above table structure, you have the two main object tables, and one relational table, called UserPolls. This will scale pretty well, for any number of polls you want.
Then, if you want to know which polls were taken by a given user, you can do:
SELECT poll_id FROM UserPolls WHERE user_id = 1
Or if you want the opposite. Polls not taken by a given user:
SELECT id FROM Polls WHERE Polls.id NOT IN (SELECT poll_id FROM UserPolls WHERE user_id = 1)
Note: this is not tested code, but you get the general idea on how to go about designing and normalizing your tables.

MySQL store demographics in multiple tables [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I have a platform with multiple clients and each has a large set of demographics that I need to store. With this in mind, it seems I could do one of two things in MySQL. Either:
Option #1: Have a large table with everyone's demographics. For example:
Table: clientDemographics
id | clientID | firstName ....
1 | 34 | John ......
2 | 12 | Fred ......
Option #2: Split out each client to having their own table:
Table: client34_demographics
id | firstName ....
1 | John ......
Table: client12_demographics
id | firstName ....
1 | Fred ......
Are there any advantages to splitting the tables out by client (efficiency, security, scalability) or disadvantages? Which of these would be a better method? Thanks!
Your second example is not a good idea (creating a table for each demographic). Instead, I would go with something more "normalized" that contains unique identifiable information in the client table, and then additional meta data (demographics) as a lookup:
Table: Clients
ClientId | FirstName | LastName | Email
-------------------------------------------------
1 | John | Smith | jsmith#email.com
Table: Demographics
DemographicId | Name
-------------------------------------------------
1 | Gender
2 | Nationality
3 | Age
Table: Clients_Demographics
CDId | ClientId | DemographicId | Value
-------------------------------------------------
1 | 1 | 1 | Male
2 | 1 | 2 | American
3 | 1 | 3 | 27
In this way you can easily sort on demographic types, demographic values, clients, etc and all the while saving space in your database, increasing query performance, and keeping your data scalable. By scalable I mean, need to add another Demographic? Just add another row to the Demographics table and then associate a value in the Clients_Demographics table with a Client. If they value is not set (i.e. no row exists) then you know that value can be seen as empty in your forms until they actually set a value.

MySQL Show recommended items

I have a news system where users can recommend the item. But now I want that when someone is viewing an item, display a list of other items that have been recommended by people who recommended the item is viewed. Something like "Users who recommended this item also recommended..."
Recommendations table
+----+------+---------+
| id | user | item |
+----+------+---------+
| 1 | 1 | 1 | User 1 recommended item 1
+----+------+---------+
| 2 | 1 | 2 |
+----+------+---------+
| 3 | 2 | 2 |
+----+------+---------+
| 4 | 3 | 3 |
+----+------+---------+
| 5 | 2 | 3 |
+----+------+---------+
Items table
+----+---------+---------+
| id | title | author |
+----+---------+---------+
| 1 |Hello... | me |
+----+---------+---------+
| 2 | Bye... | you |
+----+---------+---------+
| 3 | Hi... | me |
+----+---------+---------+
As you can see the user with ID 1, recommended item 1 and 2 and User 2 also recommended item 2, also recommended item 3.
So when someone is looking at item 2, should be displayed in the list the item 3.
Also when someone is watching item 3, you should see the list the item 2.
Do not know how to do the SQL query, I guess first I have to get the IDs of all users who recommended the article being viewed, and later, check the ID of items most repeated among the selected user IDs.
And then with those IDs of the items, get the item title or author information.
But I have no idea how to make the SQL query in a more optimized way. I would appreciate your supports.
I splitted your problem into 2 tasks:
1.Select all the users, that recommended this item except the current user:
SELECT
recommendations.user
FROM
recommendations
WHERE
recommendations.item=$item_id
AND
recommendations.user!=$user_id
2.Then we use this query as a subquery to get all the items, recommended by those users, except the currently viewed item:
SELECT
items.id,
items.title
FROM
items
INNER JOIN
recommendations
ON
items.id=recommendations.item
WHERE
recommendations.user IN
(
SELECT
recommendations.user
FROM
recommendations
WHERE
recommendations.item=$item_id
AND
recommendations.user!=$user_id
)
AND
items.id!=$item_id
GROUP BY
items.id
3.If you want to show the most recommended items first, you'll need to sort the results by number of users, who recommended the item:
SELECT
items.id,
items.title,
COUNT(*) AS cnt
FROM
items
INNER JOIN
recommendations
ON
items.id=recommendations.item
WHERE
recommendations.user IN
(
SELECT
recommendations.user
FROM
recommendations
WHERE
recommendations.item=$item_id
AND
recommendations.user!=$user_id
)
AND
items.id!=$item_id
GROUP BY
items.id
ORDER BY
cnt DESC
You could certainly accomplish your goal using Joins, but it might be a better idea to have a third table with a one to one relationship for each item and a corresponding recommended item.
This way your Mysql query for recommended items will be much more efficient querying this third table for the source item and returning a result set of all recommended items.
You can then also do a simple query to the recommendations table to get the ids of the people that recommended the items in question.
With small numbers of items, this may seem to be overkill, but as you approach large numbers, the optimization could be significant in the Mysql processing required.

Categories