Arithmetic's in an update statement in sql - mysql

I really can't wrap my head around this update sql statement.
I have the following schema for a copy table and a rental table :
copy (
copy_id ,
movie_id ,
branch_id ,
primary key (copy_id));
rental (
customer_id,
copy_id ,
outdate ,
returndate ,
primary key (customer_id,copy_id,outdate));
There will be multiple copies of the same movie. for a single movie_id, we can have multiple copy_id.
Each copy of a movie has a globally unique copy_id. So copies of 2 different movies cannot have the same copy_id.
There are multiple branches that have a copy of the movie, and certainly the copy_id will be unique.
The same movie and copy will definitely appear in rented multiple times, if it has been rented more than once.
I need to write a update statement such that - say we have 10 copies of a movie at a branch, and a time span of 30 days, so out of 300 rental days(30 days * 10 copies) the copies need to be rented out for at least 290 rental days. If this is true then I need to add another copy of that particular movie at that branch.
I tried to explain the problem in the best possible way I could. In short, I need to add a copy if the movie is rented out 90% of the time at a branch.

select branch_id, movie_id, count(*) from copy group by branch_id, movie_id
this query is branchs movie count. maybe you already have branch table, use that.
SELECT DATE_FORMAT(outdate, '%Y%m'),branch_id, movie_id, SUM(DATEDIFF(returndate , outdate)) FROM copy AS c INNER JOIN rental AS r ON c.copy_id = r.copy_id
GROUP BY DATE_FORMAT(outdate, '%Y%m'),c.branch_id, c.movie_id
this query is monthly rental time per branch and movie.
that query is basic query for calculate. and you can make logic like
if monthly rental time + new rental time > branchs rental limit
(
add new copy request
)

Related

How to get all records from two tables where doesn't exists in third

I'm looking to find a way to display a list of users that have cancelled a booking today.
The way my system works when a user cancels a booking is by adding a record into a cancellations table and deleting the record from the bookings table.
Currently I have
select distinct
members.firstname, members.lastname, cancelations.time, cancelations.groupnumber
from
members inner join cancelations on members.memberid = cancelations.memberid
where
cancelations.date = "CURRENT_DATE"
This works perfectly fine, except, this will also show if a user moves their appointment to a later/earlier time as the system will cancel then re-book.
So i believe what I would need is something like:
select distinct column names from tables where cancelations.date = "CURRENT_DATE" AND where the user hasn't got any records in the bookings table today
Tables in use (simplified)
Members - memberid, firstname, lastname
Cancelations - cancelationid, memberid, date, time, groupnumber
bookings - bookingid, memberid,date,time,groupnumber
So use NOT EXISTS() which is exactly what you are asking for :
select distinct members.firstname, members.lastname, cancelations.time, cancelations.groupnumber
from members
inner join cancelations
on members.memberid = cancelations.memberid
where cancelations.date = "CURRENT_DATE"
AND NOT EXISTS(SELECT 1 FROM bookings b
WHERE DATE(b.dateField) = DATE(cancelations.date)
AND b.memberid = member.memberid)
This checks that a record in the same day for the same member doesn't exists in booking table
I would go about this by changing your table structure for the bookings table. Instead of storing the state of a cancellation across multiple tables, I would just add a new column to bookings called isActive. This column will be set to 1 when a booking is created and will be set to 0 when one is deleted. Also when a booking is restored, it will be set to 1. This is a common technique known as "soft" deletion. It allows you to logically delete a record without actually removing it or moving it to another table. At some later point, you can archive stale deleted bookings to another table.
Here is the table structure:
CREATE TABLE bookings (`id` int PRIMARY KEY,
`memberid` int,
`isActive` tinyint
`date` datetime);
Now the query to find out if the user does not have any bookings from today is sane and straightforward:
SELECT COUNT(*)
FROM bookings
WHERE memberid = 1 AND
date = CURDATE() AND
isActive = 1
The query given by #sagi looks promising, but when you find yourself writing complex queries to answer simple business questions it might pay to think about the architecture.

Best way to store a lot of data with timestamp in MySQL

what I should do?
Imagine tennis match.
Operator pushing buttons (actions) "Ace", "Fault", "Winner", "Unforced error" etc
We have a lot of operators, matches at the same time. And we have a lot of requests to db from users (~1000 per min).
What is the best way to store match_id, player, action, time_of_action?
1) table with 1 row for every match: match_id, actions. Actions, players,timestamp coded into 1 string #of player TINYINT id of action CHAR timestamp TIMESTAMP
example: actions = "1A2014-11-28 09:01:21 2W2014-11-28 09:01:33 1F2014-11-28 09:01:49"
2) table with multiple rows for one match: id, match_id, player, action_id, current timestamp (id PRIMARY KEY)
its will be about 250K rows after one day (300 per match * 40 matches in 1 tournament * 20 tournaments per day)
what is better: a lot of rows and a lot of requests SELECT player, action_id, timestamp FROM scores WHERE match_id = N
or
same number of requests, less rows ( /300 ) but much bigger data in rows?
sry for my ugly language, I hope you understand me, if not, tell me
add:
Im going to use it for match statistics on live or after match.
Users open page Statistics of match Federer - Nadal and every 10-30 seconds page refreshing
Example: http://www.wimbledon.com/en_GB/slamtracker/slamtracker.html?ts=1419259452680&ref=www.wimbledon.com/en_GB/slamtracker/index.html&syn=none&
I suggest you create reference tables called
match match_id, name, venue A row for each distinct match
player player_id, name A row for each distinct player
action action_id, name This is a codelist 1=Ace 2=Fault, etc.
These tables will be relatively static.
Then, I suggest you create an event table containing the following items in the following order.
match_id
ts (TIMESTAMP)
action_id
player_id
You should include all four of these columns in a composite primary key, in the order I have shown them.
Every time your scorers record an action you'll insert a new row to this table.
When you want to display the actions for a particular match, you can do this:
SELECT event.ts,
action.name AS action,
player.name AS player
FROM event
JOIN player ON event.player_id = player.player_id
JOIN action ON event.action_id = action.action_id
WHERE event.match_id = <<whatever match ID>>
ORDER BY event.match_id, event.ts
Because of the order of columns in the composite primary key on the event table, this kind of query will be very efficient even when you're inserting lots of new rows to that table.
MySQL is made for this kind of application. Still, when your site begins to receive tons of user traffic, you probably should arrange to run these queries just once every few seconds, cache the results, and use the cached results to send information to your users.
If you want to retrieve the match IDs for all the matches presently active (that is, with an event within the last ten minutes) you can do this.
SELECT DISTINCT match.id, match.name, match.venue
FROM event
JOIN match on event.match_id = match.match_id
WHERE event.ts >= NOW() - INTERVAL 10 MINUTE
If you need to do this sort of query a lot, I suggest you create an extra index on (ts, match_id).

JOINing tables while ignoring duplicates

So, let's say I have a hash/relational table that connects users, teams a user can join, and challenges in which teams participate (teams_users_challenges), as well as a table that stores entered data for all users in a given challenge (entry_data). I want to get the average scores for each user in the challenge (the average value per day in a given week). However, there is a chance that a user will somehow join more than one team erroneously (which shouldn't happen, but does on occasion). Here is the SQL query below that gets a particular user's score:
SELECT tuc.user_id, SUM(ed.data_value) / 7 as value
FROM teams_users_challenges tuc
LEFT JOIN entry_data ed ON (
tuc.user_id = ed.user_id AND
ed.entry_date BETWEEN '2013-09-16' AND '2013-09-22'
)
WHERE tuc.challenge_id = ___
AND tuc.user_id = ___
If a user has mistakenly joined more than one team, (s)he would have more than one entry in teams_users_challenges, which would essentially duplicate the data retrieved. So if a user is on 3 different teams for the same challenge, (s)he would have 3 entries in teams_users_challenges, which would multiply their average value by 3, thanks to the LEFT JOIN that automatically takes in all records, and not just one.
I've tried using GROUP BY, but that doesn't seem to restrict the data to only one instances within teams_users_challenges. Does anybody have any ideas as to how I could restrict the query to only take in one record within teams_users_challenges?
ADDENDUM: The columns within teams_users_challenges are team_id, user_id, and challenge_id.
If this is a new empty table, you can express your 'business rule' that a user should only join one team per challenge as a unique constraint in SQL:
alter table teams_users_challenges
add constraint oneUserPerTeamPerChallenge
unique (
user_id
, team_id
, challenge_id
);
If you can't change the table, you'll need to group by user and team and pick a single challenge from each group in the query result. Maybe pick just the latest challenge.
I can't test it, but if you can't clean up the data as Yawar suggested, try:
SELECT tuc.user_id, SUM(ed.data_value) / 7 as value
FROM entry_data ed
LEFT JOIN
(
select tuc.user_id, tuc.challenge_id from teams_users_challenges tuc group by tuc.user_id, tuc.challenge_id
) AS SINGLE_TEAM
ON SINGLE_TEAM.user_id = ed.user_id AND
ed.entry_date BETWEEN '2013-09-16' AND '2013-09-22'
WHERE tuc.challenge_id = ___
AND tuc.user_id = ___

Database design for a Fantasy league

Here's the basic schema for my database
Table user{
userid numeber primary key,
count number
}
Table player{
pid number primary key,
}
Table user-player{
userid number primary key foreign key(user),
pid number primary key foreign key(player)
}
Table temp{
pid number primary key,
points number
}
Here's what I intend to do...
After every match the temp table is updated which holds the id of players that played the last match and the points they earned.
Next run a procedure that will match the pid from temp table with every uid of user-player table having the same pid.
add the points from temp table to the count of user table for every matching uid.
empty temp table.
My questions is considering 200 players and 10000 users,Will this method be efficient?
I am going to be using mysql for this.
People often seem to be worried about performance for small databases. Let the DBMS do what it is designed to do. If you find in practice - or preferably under load testing - that you have a performance problem, then take steps to deal with it. Otherwise don't pre-optimize!
Instead of using a temporary table to store one batch of player scores, store all player scores in a tranactional table.
Remove the user.count column and replace your temp table with something like this:
Table player_points{
pid number primary key,
match_date datetime primary key,
points number
}
With this you can easily calculate any user's score. You can even recalculate any user's score as of a given date. This is much more powerful and much simpler to maintain. Keeping a current snapshot only makes it impossible to manage should anything go wrong or should one of your users challenge their score.
This query gives you the scores for all users. You can add filters to it to do other things like finding the score for a single user or showing a leader board.
select
U.userid as UserID
, sum(S.points) as TotalScore
from user S
inner join user-player J
on S.userid = J.userid
inner join player_points S
on J.pid = S.pid
group by
U.userid
This query would give you a leader board:
select
U.userid as UserID
, sum(S.points) as TotalScore
from user S
inner join user-player J
on S.userid = J.userid
inner join player_points S
on J.pid = S.pid
group by
U.userid
order by TotalScore desc
limit 10
This query would give you points awarded to a user by date, which you could graph as-is or cumulatively, for example.
select
S.match_date as MatchDate
, sum(S.points) as TotalScore
from user-player J
inner join player_points S
on J.pid = S.pid
where J.userid = 123 -- The user ID you want.
group by
S.match_date
order by S.match_date

Grouping MySQL results to only allow a record to add to the total if the last one found is over two hours old

I am currently tracking actions performed by employees in a table, which has three rows: id, user_id, and action_time, customer_id. In order to track performance, I can simply pick an employee on a date, and count the actions they've performed, easy peasy.
SELECT COUNT(DISTINCT `customer_id`) AS `action_count`
FROM `actions`
WHERE `user_id` = 1
AND DATE(`action_time`) = DATE(NOW())
However, I now wish to make it so that actions performed more than two hours apart will class as two actions towards the total. I've looked into grouping by HOUR() / 2 but an action performed at 9:59 and 10:01 will count as two, not quite what I want.
Anyone have any ideas?
You must self-JOIN the actions table, try something like this:
SELECT COUNT(DISTINCT id) FROM (
SELECT a1.id, ABS(UNIX_TIMESTAMP(a1.action_time) - UNIX_TIMESTAMP(a2.action_time))>=7200
AS action_time_diff FROM actions a1 JOIN actions a2 ON a1.user_id=a2.user_id) AS t
WHERE action_time_diff = 1
Not sure if this works, perhaps you should provide more exact details about the table structure.