I am working on a project and wanted to have a quick look by you people if the schema or query needs some changes or does it totally needs to be changed.
The project is about creating a ranking system for the badminton teams where the ranking will be based on the scoring rules in a tournament
+2 Points/match would be awarded to the winning team.
An Extra +1 Point would be awarded to the winning team if the match was a Quarter-finals.
An Extra +2 Points would be awarded to the winning team if the match was a Semi-finals.
An Extra +5 Points would be awarded to the winning team if the match was a Final.
Winning all pool matches will add 4 points to your Team score.
Winning more than 3 tournaments will add 15 points to your team.
I started by creating the following tables
Players
+----------+-------------------------------------+------+-----+--------------
| Field | Type | Null | Key | +----------+-------------------------------------+------+-----+--------------
| id | int(11) | NO | PRI |
| name | varchar(250) | NO | |
| image | text | YES | |
| plays | enum('RH','LH') | NO | |
| added_on | datetime | NO | |
| status | enum('active','inactive','retired') | NO | | +----------+-------------------------------------+------+-----+--------------
Teams
+------------+----------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(150) | NO | UNI | NULL | |
| image | text | YES | | NULL | |
| status | enum('active','in-active') | NO | | active | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
| updated_on | datetime | YES | | NULL | |
+------------+----------------------------+------+-----+-------------------+----------------+
Player To Teams
+-----------+---------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| player_id | int(11) | NO | MUL | NULL | |
| team_id | int(11) | NO | | NULL | |
| status | enum('active','inactive') | NO | | NULL | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
+-----------+---------------------------+------+-----+-------------------+----------------+
Tournaments
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| year | int(4) | NO | | NULL | |
+-------+--------------+------+-----+---------+----------------+
Matches
+---------------+---------------------------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------------------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| team_one | int(11) | NO | MUL | NULL | |
| team_two | int(11) | NO | | NULL | |
| winner_id | int(11) | NO | | NULL | |
| tournament_id | int(11) | NO | MUL | 1 | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
| match_type | enum('pool','quarter','semi','final') | NO | | pool | |
| sets | smallint(2) | NO | | 1 | |
+---------------+---------------------------------------+------+-----+-------------------+----------------+
Match Score
+----------+-------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| match_id | int(11) | NO | MUL | NULL | |
| team_id | int(11) | NO | MUL | NULL | |
| set_num | enum('1','2','3') | NO | | NULL | |
| score | smallint(2) | NO | | NULL | |
| added_on | datetime | NO | | CURRENT_TIMESTAMP | |
+----------+-------------------+------+-----+-------------------+----------------+
Pools
+---------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | UNI | NULL | |
| tournament_id | int(11) | NO | MUL | NULL | |
+---------------+-------------+------+-----+---------+----------------+
One thing that you will notice that i have not saved rank scoring anywhere and i am calculating it on runtime using the following query
SELECT
T.id, T.name, T.status,
IFNULL(T.image,'no-image.png') as DP,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.team_one = T.id OR M.team_two = T.id)) as played,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id = T.id) as won,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one = T.id OR M.team_two = T.id)
AND (M.winner_id != T.id))) as lost,
(
((SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id = T.id) * 2) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'quarter'
AND M.winner_id = T.id)) * 2) + 1) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'semi'
AND M.winner_id = T.id)) * 2) + 2) +
(((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type = 'final' AND M.winner_id = T.id)) * 2) + 5)) as Points
FROM
badminton_teams T
ORDER BY
(Points) DESC;
First
is it right to calculate the scoring on runtime using query or
should I save update it every time I save a match result in the database
or
should I schedule a cron job for this purpose
Edit:
Updated the query to the following
SELECT
T.id, T.name, T.status,
IFNULL(T.image,'no-image.png') as DP,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) and M.winner_id IS NOT NULL)) as played,
(SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id=T.id) as won, (SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) AND (M.winner_id!=T.id))) as lost,
((SELECT (SUM(BMS.points_won)-SUM(BMS.points_lost))
FROM
badminton_match_score BMS
JOIN badminton_matches M ON (M.id=BMS.match_id)
where M.team_one=T.id OR M.team_two=T.id and M.winner_id is not null)/(SELECT COUNT(*)
FROM badminton_matches M
WHERE ((M.team_one =T.id or M.team_two = T.id) and M.winner_id IS NOT NULL))) AS AVG_SCORE,
(
((SELECT COUNT(*)
FROM badminton_matches M
WHERE M.winner_id=T.id)*2) +
(SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='quarter' AND M.winner_id=T.id))
+
((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='semi' AND M.winner_id=T.id))*2)
+
((SELECT COUNT(*)
FROM badminton_matches M
WHERE (M.match_type='final' AND M.winner_id=T.id))*5)
)
as Points
FROM badminton_teams T
order by (Points) DESC, lost ASC, AVG_SCORE DESC
is it right to calculate the scoring on runtime using query
If you want it to be a real time application. In my opinion, yes if the points are not accumulated and will reset after a game. For Sql performance issues.
should i save update it every time i save a match result in the database
Updates are for modification only of data, saving the data after a match for history will be sufficient.
should i schedule a cron job for this purpose
If in terms of larger databases and productivity, yes.
Related
I have 3 tables: actors, movies and cast:
mysql> desc actors;
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| id | int | NO | PRI | 0 | |
| full_name | varchar(200) | YES | | NULL | |
| gender | varchar(1) | YES | | NULL | |
+-----------+--------------+------+-----+---------+-------+
mysql> desc movies;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int | NO | PRI | 0 | |
| title | varchar(100) | YES | | NULL | |
| year | int | YES | | NULL | |
| genre | varchar(100) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
and
mysql> desc cast;
+----------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+------+------+-----+---------+-------+
| actor_id | int | YES | MUL | NULL | |
| movie_id | int | YES | MUL | NULL | |
| salary | int | YES | | NULL | |
+----------+------+------+-----+---------+-------+
The connection between tables is: cast.movie_id = movies.id and actors.id = cast.actor_id
The question: what is the average career longevity(years between the first film and the last film) of
actors who were at least in five films during their career?
I have tried to list actors ordered by the number of films, they took part in:
mysql> select full_name, count(title) as movie_title from cast, actors, movies
-> where cast.movie_id = movies.id and actors.id = cast.actor_id
-> group by full_name
-> order by movie_title
-> desc limit 2;
+--------------------------+-------------+
| full_name | movie_title |
+--------------------------+-------------+
| Kevin Bacon | 9 |
| Bill Paxton | 3 |
...
If what I've done is correct (not sure), we have only one such actor, so the question will be how to find this span
Will be very grateful for any advice!
How about:
SELECT AVG(YearsActive) TotalAverage
FROM (
-- by actor
SELECT a.id AS ActorId,
-- GROUP_CONCAT(a.full_name) AS ActorName,
(MAX(m.year) - MIN(m.year)) AS YearsActive
FROM actors a
INNER JOIN cast c ON a.id = c.actor_id
INNER JOIN movies m ON c.movie_id = m.id
GROUP BY a.id
HAVING COUNT(0) >= 5
) B
;
I have two tables:
mysql> DESCRIBE swaps;
+-------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| user1_id | int(11) | NO | | NULL | |
| user2_id | int(11) | NO | | NULL | |
| hasto | int(11) | NO | | NULL | |
| requested | datetime | NO | | NULL | |
| accepted | datetime | YES | | NULL | |
| swapped1 | datetime | YES | | NULL | |
| swapped2 | datetime | YES | | NULL | |
| rejected | datetime | YES | | NULL | |
| rejected_by | int(11) | YES | | NULL | |
+-------------+----------+------+-----+---------+----------------+
mysql> DESCRIBE messages;
+-----------+----------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------+------+-----+-------------------+----------------+
| msg_id | int(11) | NO | PRI | NULL | auto_increment |
| sender_id | int(11) | NO | | NULL | |
| msg | text | NO | | NULL | |
| msg_time | datetime | NO | | CURRENT_TIMESTAMP | |
| swap_id | int(11) | NO | | NULL | |
| seen | datetime | YES | | NULL | |
+-----------+----------+------+-----+-------------------+----------------+
and query that I adjusted from this question
SELECT s.*, m.*
FROM swaps as s
JOIN messages as m
ON (s.id= m.swap_id AND m.msg_time=
(SELECT MAX(msg_time) FROM messages WHERE messages.swap_id = s.id));
as a result I single row for every swap and information about last sent message within this swap. I want to add the count of messages that have not jet been seen (m.seen IS NULL).
I tried different approaches but always get error. What I want is to add count of messages in corresponding swap with seen IS NULL to my result set. Would appreciate any suggestions.
You can add the count as a subquery:
SELECT s.*, m.*,
(SELECT COUNT(*) FROM messages m2 WHERE m2.seen IS NULL) as seen_is_null
FROM swaps s JOIN
messages m
ON s.id= m.swap_id AND
m.msg_time =(SELECT MAX(m2.msg_time) FROM messages m2 WHERE m2.swap_id = s.id));
It seems curious to be counting over all messages, but that is what the question asks for. You can, of course, introduce a correlation clause to count for a given swap or something else.
I have the following database table:
+----------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+----------------+
| results_id | int(11) | NO | PRI | NULL | auto_increment |
| community_id | int(11) | NO | | NULL | |
| player1_id | int(11) | NO | | NULL | |
| player1_name | varchar(50) | NO | | NULL | |
| player1_team | varchar(50) | NO | | NULL | |
| player1_goals | int(11) | NO | | NULL | |
| player1_result | varchar(3) | NO | | NULL | |
| player2_goals | int(11) | NO | | NULL | |
| player2_result | varchar(3) | NO | | NULL | |
| player2_id | int(11) | NO | | NULL | |
| player2_name | varchar(50) | NO | | NULL | |
| player2_team | varchar(50) | NO | | NULL | |
| player1_pts | int(11) | NO | | NULL | |
| player2_pts | int(11) | NO | | NULL | |
| date | date | NO | | NULL | |
+----------------+-------------+------+-----+---------+----------------+
I want to run a MySQL query that will tell me who a player has scored the most goals against.
That player can either be player1 or player2.
I imagine it being something like this:
select member, goals, count(*) Total
from
(
select player2_name as member, player1_goals as goals
from results
WHERE player1_id = 2
union all
select player1_name as member, player2_goals as goals
from results
WHERE player2_id = 2
) AS T
group by goals
order by Total desc
Limit 1
This gives a result:
member | goals | Total
Jamie Charles | 2 | 11
Jamie Charles | 0 | 8
Jamie Charles | 3 | 5
Jamie Charles | 1 | 4
Jamie Charles | 5 | 1
But it looks like it is saying how many of that number of goals has occurred. So the correct result would be (2x11)+(3*5)+(1*4)+(5*1)
What is the correct Syntax for this query?
You need to group by member, use the sum aggregate function to get the sum of all the goals. You do not need the order
select member, sum(goals), count(*) Total_goal_records
from
(
select player2_name as member, player1_goals as goals
from results
WHERE player1_id = 2
union all
select player1_name as member, player2_goals as goals
from results
WHERE player2_id = 2
) AS T
group by member
order by Total desc;
I'm trying to output the amount of discount given to a customer by finding the difference, but I keep getting Error #1109 Unknown table 'item' in field list when I do have a table named item in my current DB.
I'm using this query:
SELECT orderpay.orderid , cust.name , (orderpay.totalpay - actualCost) as `discount amount`
FROM cust , orderpay , tran , item,
(SELECT sum(item.unitprice * tran.quantity) ) as actualCost
WHERE item.itemid = tran.itemid AND (orderpay.totalpay - actualCost) > 0
GROUP BY item.itemid
Here are my table structures:
cust
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| custid | int(5) | NO | PRI | 0 | |
| name | varchar(22) | YES | | NULL | |
| email | varchar(33) | YES | | NULL | |
| address | varchar(29) | YES | | NULL | |
| city | varchar(21) | YES | | NULL | |
| country | varchar(14) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
item
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| itemid | int(5) | NO | PRI | 0 | |
| name | varchar(94) | YES | | NULL | |
| unitprice | decimal(6,2) | YES | | NULL | |
| cat | varchar(12) | YES | | NULL | |
+-----------+--------------+------+-----+---------+-------+
orderpay
+----------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------------+------+-----+---------+-------+
| orderid | int(5) | NO | PRI | 0 | |
| date | varchar(10) | YES | | NULL | |
| custid | int(5) | YES | | NULL | |
| cardnum | varchar(25) | YES | | NULL | |
| cardtype | varchar(25) | YES | | NULL | |
| totalpay | decimal(10,2) | YES | | NULL | |
+----------+---------------+------+-----+---------+-------+
tran
+----------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------+------+-----+---------+-------+
| orderid | int(5) | NO | PRI | 0 | |
| itemid | int(5) | NO | PRI | 0 | |
| quantity | int(2) | YES | | NULL | |
+----------+--------+------+-----+---------+-------+
Consider changing your query like below
SELECT *,
(totalpay - computed_price) as `discount amount`
FROM
(
SELECT orderpay.orderid ,
cust.name ,
orderpay.totalpay ,
sum(item.unitprice * `tran`.quantity) as computed_price
FROM cust JOIN `tran` ON item.itemid = tran.itemid
JOIN orderpay ON cust.some_column = orderpay.some_column
JOIN item ON cust.some_column = item.some_column
GROUP BY item.itemid
) tab
WHERE (totalpay - computed_price) > 0
Select totalpaid.custid, (totalprices.total - totalpaid.total) as `discount` FROM
(
SELECT sum(totalpay) as total, custid
FROM orderpay
GROUP BY custid
) as totalpaid JOIN
(
SELECT sum(item.unitprice * tran.quantity) as total, orderpay.custid
FROM orderpay JOIN tran ON orderpay.orderid=tran.orderid JOIN item ON tran.itemid item.itemid
GROUP BY orderpay.custid
) as totalprices ON totalpaid.custid = orderpay.custid
As a side note, you should not use this to calculate the total as if prices changed (and they do change in time) you calculation will be all messed up. Instead you should remember the discount for each order when you create it.
VERY IMPORTANT
Also it scares me that you have a field called cardnum | varchar(25) you should never ever keep the CC numbers.
I have three tables named User,Role and Balance_updates. User table hold info about user,Role depicts type of user like Customer,Admin,Manager and Balance_updates store all transaction regarding balance i.e it store history about transaction related to balance.
Tables
User
+-----------------------+--------------+------+-----+-------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+--------------+------+-----+-------------------+-------+
| username | varchar(20) | NO | PRI | NULL | |
| password | varchar(32) | NO | | NULL | |
| email | varchar(50) | YES | | NULL | |
| role_id | int(11) | NO | MUL | NULL | |
| mobile_wallet_balance | double(20,2) | NO | | 0.00 | |
| merit_point | bigint(20) | YES | | NULL | |
| status | int(11) | NO | | NULL | |
| is_auto_btm_enabled | tinyint(1) | YES | | 0 | |
| created_at | datetime | YES | | NULL | |
| updated_at | timestamp | YES | | CURRENT_TIMESTAMP | |
| gender | varchar(20) | YES | | NULL | |
| validity | date | YES | | NULL | |
| status_desc | text | YES | | NULL | |
+-----------------------+--------------+------+-----+-------------------+-------+
Role
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(25) | NO | UNI | NULL | |
| description | varchar(255) | YES | | NULL | |
| value | varchar(25) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
Balance_updates
+------------+--------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(15) | NO | | NULL | |
| role_id | int(11) | YES | | NULL | |
| amount | double(20,2) | YES | | NULL | |
| updated_at | timestamp | NO | | CURRENT_TIMESTAMP | |
+------------+--------------+------+-----+-------------------+----------------+
Data in Balance_updates
Objective
I want to generate all users and their respective balance in any given date;
For example if i want the balance statement for a given date 2012-12-28 it should generate latest balance from Balance_updates.
What I have tried
SELECT DISTINCT (
u.username
), r.value, u.amount AS `amount`
FROM Balance_updates u
INNER JOIN Role r ON u.role_id = r.id
WHERE u.amount > 0.0 && UNIX_TIMESTAMP( u.updated_at ) < UNIX_TIMESTAMP( '2013-1-3 23:59:59' )
ORDER BY r.value, UNIX_TIMESTAMP( u.updated_at ) DESC
RESULT and PROBLEM
As you expect it is returning all values from Balance_updates i.e every other User transaction recorded.
Question:
1.How can i achieve latest balance statement of a user in a particular date.If in that date user balance is not changed then show his balance in previous time last changed.
Any help will be appreciated;
Not tested, but I have the feeling this could work (and could be optimised).
SELECT DISTINCT u.username, r.value, u.amount AS `amount`
FROM Balance_updates u
INNER JOIN Role r ON u.role_id = r.id
WHERE u.updated_at = (
SELECT MAX(inner_u.updated_at)
FROM Balance_updates AS inner_u
WHERE
inner_u.username = u.username
&& amount > 0
&& UNIX_TIMESTAMP( inner_u.updated_at ) < UNIX_TIMESTAMP( '2013-1-3 23:59:59' )
)
ORDER BY r.value, UNIX_TIMESTAMP( u.updated_at ) DESC
Just one off-topic piece of advice:
add a surrogate key to table User (e.g. User.id); make it the primary key
replace column Balance_updates.username with (e.g.) Balance_updates.user_id, and store a reference to User.id (a foreign key).
or
change column Balance_updates.username to a VARCHAR(20) to match the type of User.username