Database design - one to one relation with repeat possible? - mysql

I have to design a simple relation
1 "Workout" has multiple "Interval"
1 "Interval" is present in only 1 "Workout", but it can be repeated multiple time in a "Workout"
I was thinking having this structure:
Interval
-id
-workout_id (foreign key)
-seq_workout
Workout
-id
-name
But with this structure, if an "Interval" is present many time in a "Workout", I have to insert multiple row of this Interval in the table with a different "seq_workout" (position where the interval is in the workout). I find this bad for using DB space that could be saved otherwise.
I could use a third table (interval_workout_position)
Where I put the Interval id, the Workout id, and the position of the interval in the Workout. That way I could put the same interval multiple time in a Workout)
Is there another solution, because I find using 3 table may be overkill for this?
Basically i'm just trying to reprensent an ArrayList with repeat possible (Workout has a QList of Interval ), for those familiar with Qt or ArrayList in other langage.
Thank you!

This is a one-to-many relationship.
Your relational design is sound. The table design you have supports these requirements;
An Interval occurs in exactly 1 Workout.
1 Workout has zero, one or more Intervals.
An Interval has a position (sequence) within a Workout.
There is no need for a third table, unless you are introducing another entity.
Update:
The table definition shown in the question is different than the table design added in the comment.
If you want to model a many-to-many relationship, you would use three tables.
interval
id
message_en
duration
etc.
workout
id
name
workout_interval
id
workout_id (fk references workout.id)
seq_workout
interval_id (fk references interval.id)
etc.
This model is a many-to-many.
The workout_interval table is the "relationship" between the Workout and the Interval.
With this model
An Interval can appear in zero, one or more Workout, and a Workout can have zero, one or more Interval.
Any attributes that are specific to a particular Interval within a Workout can be added to the relationship table. Attributes of just the Interval (like a label or whatever), which doesn't change, those would be on the Interval table.
For example, if you are tracking workout results, you would record the weight and number of reps completed on the workout_interval table.
Update 2:
Given that you want an Interval to be used only in a single Workout, my the model in my previous update could be used, but it doesn't get you the constraint...
This model would provide that restriction. interval is a child of workout, and interval_seq is a child of interval.
workout
id
name
interval
id
workout_id (fk references workout.id)
message_en
etc.
interval_seq
id
interval_id (fk references interval.id)
seq_ (position within workout sequence 1,2,3,...)
etc.
To get the intervals within a workout in sequence, with repeats:
SELECT w.id
, s.seq_
, i.*
, s.*
FROM workout w
JOIN interval i
ON i.workout_id = w.id
JOIN interval_seq s
ON s.interval_id = i.id
WHERE w.id
ORDER BY w.id, s.seq_

Just to say that I chose the solution with 3 tables from "spencer7593" :
-interval
-workout
-interval_workout (link between interval and workout)
It was the most natural way for me, I'm missing one small constraint (1 interval can only be in one workout) but I will be managing the insert in the table so I don't have to worry about that.
Now here is an example of a query I do to get all interval's workout for a specific user in my database, this query return me a xml file from my Rest webService that my application use to build workouts specific for a user:
Now I need to see if Json is better than Xml in my application (using QXmlStreamReader from Qt now) and if there's a better way to code that query (currently hard-coded in a php page, hard to maintain) Thanks for your help!
└(°ᴥ°)┘ - To the moon! - └(°ᴥ°)┘
/* GET ALL INTERVAL WITH DETAILS IN SUBSCRIBED WORKOUT FOR USER=X */
SELECT
/* plan */
p.id plan_id, p.name_en plan_name_en, p.name_en plan_name_fr,
/* workout type */
wt.id workout_type_id, wt.type_en workout_type_en, wt.type_fr workout_type_fr,
/* workout */
w.id workout_id, w.name_en workout_name_en, w.name_fr workout_name_fr,
w.descrip_en workout_descrip_en, w.descrip_fr workout_descrip_fr, w.creator workout_creator,
/* seq_workout */
iw.seq_workout,
/* intervalle */
i.id intervalle_id, i.duration intervalle_duration, i.msg_en intervalle_msg_en, i.msg_fr intervalle_msg_fr,
/* intervalle : power */
i.power_start intervalle_power_start, i.power_end intervalle_power_end, i.power_range intervalle_power_range, i.power_left intervalle_power_left,
/* intervalle : cadence */
i.cadence_start intervalle_cadence_start, i.cadence_end intervalle_cadence_end, i.cadence_range intervalle_cadence_range,
/* intervalle : hr */
i.hr_start intervalle_hr_start, i.hr_end intervalle_hr_end, i.hr_range intervalle_hr_range,
/* intervalle type */
it.id intervalle_type_id, it.type_en intervalle_type_en, it.type_fr intervalle_type_fr,
/* intervalle : step type */
isPower.id intervalle_steptype_power_id, isPower.type_en intervalle_steptype_power_type_en, isPower.type_fr intervalle_steptype_power_type_fr,
isCadence.id intervalle_steptype_cadence_id, isCadence.type_en intervalle_steptype_cadence_type_en, isCadence.type_fr intervalle_steptype_cadence_type_fr,
isHr.id intervalle_steptype_hr_id, isHr.type_en intervalle_steptype_hr_type_en, isHr.type_fr intervalle_steptype_hr_type_fr
FROM user u
INNER JOIN user_groupe ug
ON u.id = ug.user_id
INNER JOIN groupe g
ON ug.groupe_id = g.id
INNER JOIN groupe_plan gp
ON g.id = gp.groupe_id
INNER JOIN plan p
ON gp.plan_id = p.id
INNER JOIN workout w
ON p.id = w.plan_id
INNER JOIN workout_type wt
ON wt.id = w.workout_type_id
INNER JOIN intervalle_workout iw
ON w.id = iw.workout_id
INNER JOIN intervalle i
on i.id = iw.intervalle_id
INNER JOIN intervalle_type it
on i.intervalle_type_id = it.id
INNER JOIN intervalle_steptype isPower
on isPower.id = i.power_steptype_id
INNER JOIN intervalle_steptype isCadence
on isCadence.id = i.cadence_steptype_id
INNER JOIN intervalle_steptype isHr
on isHr.id = i.hr_steptype_id
WHERE u.id = 1
ORDER BY iw.workout_id, iw.seq_workout;

Related

MYSQL: Error 1054 - Unknown Column

Before we start, I apologize for my MYSQL novice status. I'm trying to self-teach and struggling a bit with basic the basic structure.
Background on my question: Consider a company that issues bills once a quarter... and roughly two months after the end of the last quarter. I have a table (Quarter_Identify) that has several columns:
Quarter_Start EX: 01-01-2010
Quarter_End EX: 03-31-2010
Quarter_Ident EX: 1000 <--- iterating number for each quarter (next
quarter will be 1001)
Date_Billed This is the field I'm trying to populate from another table
The other table (Billing_List_1) contains:
Date_Billed EX: 05-23-2010
Lots of other nonsense related to the customers
We take all the orders during the quarter, and bill about 60 days after it ends. So, in the example above, the 5-23-2010 billing would be related to the Jan - Mar quarter (we bill really late). I would like to take this date and populate it back as the Date_Billed associated with Quart_Ident "1000".
I'm fairly close and from my research I think I'm running into the issue that my "Where" clause includes a reference to the as-yet not created table "Skyline". The "skyline" table gets everything together, but is essentially off by a month (I gave up trying to figure out the DateDiff function). So, I use the bottom piece to offset the result by one and get the right answer... except that it tells me I have an unknown column in my where clause (error 1054) the issue.
Select * from
(select Billing_List_1.date_billed, quarter_identify.quarter_start,
quarter_identify.quarter_end, quarter_identify.quarter_ident from Billing_List_1
join quarter_identify
on Billing_List_1.date_billed > quarter_identify.quarter_start
and Billing_list_1.date_billed < quarter_identify.quarter_end)
as SKYLINETABLE;
update quarter_identify A
set A.date_Billed = SKYLINETABLE.date_Billed
where A.quarter_ident = SKYLINETABLE.quarter_ident - 1
Any thoughts would be much appreciated. Have a great evening all.
Solution per TEEZ: Thanks again for the great help.
update quarter_identify A Left join
(Select * from
(select Billing_List_1.date_billed,
quarter_identify.quarter_start,
quarter_identify.quarter_end,
quarter_identify.quarter_ident from billing_list_1
join quarter_identify
on Billing_list_1.date_billed > quarter_identify.quarter_start
and Billing_list_1.date_billed < quarter_identify.quarter_end)
as T)
as SKYLINETABLE on 1
set A.date_billed = SKYLINETABLE.date_billed
where A.quarter_ident = SKYLINETABLE.quarter_ident - 1
I think you are wrong. what is SKYLINE table in update query?
You are joining tables in update query but table is not specified. you should use your first query in join with update query.
you need to use join SKYLINETABLE in join with your update query.
Like below:
update quarter_identify A left join (Select * from
(select Billing_List_1.date_billed, quarter_identify.quarter_start,
quarter_identify.quarter_end, quarter_identify.quarter_ident from Billing_List_1
join quarter_identify
on Billing_List_1.date_billed > quarter_identify.quarter_start
and Billing_list_1.date_billed < quarter_identify.quarter_end)) as SKYLINETABLE on[... specify on condition....]
set A.date_Billed = SKYLINETABLE.date_Billed
where A.quarter_ident = SKYLINETABLE.quarter_ident - 1
Please do required changes

Mysql subquery with joins

I have a table 'service' which contains details about serviced vehicles. It has an id and Vehicle_registrationNumber which is a foreign key. Whenever vehicle is serviced, a new record is made. So, for example if I make a service for car with registration ABCD, it will create new row, and I will set car_reg, date and car's mileage in the service table (id is set to autoincreament) (e.g 12 | 20/01/2012 | ABCD | 1452, another service for the same car will create row 15 | 26/01/2012 | ABCD | 4782).
Now I want to check if the car needs a service (the last service was either 6 or more months ago, or the current mileage of the car is more than 1000 miles since last service), to do that I need to know the date of last service and the mileage of the car at the last service. So I want to create a subquery, that will return one row for each car, and the row that I'm interested in is the newest one (either with the greatest id or latest endDate). I also need to join it with other tables because I need this for my view (I use CodeIgniter but don't know if it's possible to write subqueries using CI's ActiveRecord class)
SELECT * FROM (
SELECT *
FROM (`service`)
JOIN `vehicle` ON `service`.`Vehicle_registrationNumber` = `vehicle`.`registrationNumber`
JOIN `branch_has_vehicle` ON `branch_has_vehicle`.`Vehicle_registrationNumber` = `vehicle`.`registrationNumber`
JOIN `branch` ON `branch`.`branchId` = `branch_has_vehicle`.`Branch_branchId`
GROUP BY `service`.`Vehicle_registrationNumber` )
AS temp
WHERE `vehicle`.`available` != 'false'
AND `service`.`endDate` <= '2011-07-20 20:43'
OR service.serviceMileage < vehicle.mileage - 10000
SELECT `service`.`Vehicle_registrationNumber`, Max(`service`.`endDate`) as lastService,
MAX(service.serviceMileage) as lastServiceMileage, vehicle.*
FROM `service`
INNER JOIN `vehicle`
ON `service`.`Vehicle_registrationNumber` = `vehicle`.`registrationNumber`
INNER JOIN `branch_has_vehicle`
ON `branch_has_vehicle`.`Vehicle_registrationNumber` = `vehicle`.`registrationNumber`
INNER JOIN `branch`
ON `branch`.`branchId` = `branch_has_vehicle`.`Branch_branchId`
WHERE vehicle.available != 'false'
GROUP BY `service`.`Vehicle_registrationNumber`
HAVING lastService<=DATE_SUB(CURDATE(),INTERVAL 6 MONTH)
OR lastServiceMileage < vehicle.mileage - 10000
;
I hope I have no typo in it ..
If instead of using * in the subquery you specify the fields you need (which is always good practice anyway), most databases have a MAX() function that returns the maximum value within the group.
Actually, you don't even need the subquery. You can do the joins and use the MAX in the SELECT statement. Then you can do something like
SELECT ...., MAX('service'.'end_date') AS LAST_SERVICE
...
GROUP BY 'service'.'Vehicle_registrationNumber'
Or am I missing something?

MySQL query construction

I have a query problem. I have a table of agents that do things. I keep track of the things they do in an events table. I want to keep my agents busy but not too busy, so I need a query that will return me a group of agents that have done no more that 10 events in the past 10 minutes and no more than 400 events in the past 24 hours. And from this pool of available agents I can choose one to give something to do
So my agent table looks something like:
Agent table
AgentID. AgentName
1 Bob
2 Sue
Event Table
Event ID. Agent ID. Event Timestamp
1 2 1319525462
2 1 1319525462
3 2 1319525462
Obviously these tables are just to give the form of the db. What I need generally and have not been able to figure out is how to select a group of agents from a join that returns a group of agents that have done no more than 10 events in the past 10 min and no more than 400 events in the past 24 hours. My actual tables are more complex, but I am just looking for a general principle on how to structure a query that would return the desired result. Thanks ahead of time for the help!
**UPDATE
building on Benoit's answere I came up with this:
SELECT DISTINCT username FROM campaign_agents
LEFT OUTER JOIN (SELECT count(event_index) myevents, field_event_agent_value FROM new_event WHERE field_event_time_value BETWEEN 1320206138 AND 1320292538 GROUP BY field_event_agent_value ) last_24_hours
ON last_24_hours.field_event_agent_value = campaign_agents.username
LEFT OUTER JOIN (SELECT count(event_index) myevents, field_event_agent_value FROM new_event WHERE field_event_time_value BETWEEN 1320291938 AND 1320292538 GROUP BY field_event_agent_value ) last_10_mins
ON last_10_mins.field_event_agent_value = campaign_agents.username
WHERE last_24_hours.myevents < 550 AND last_10_mins.myevents < 10
But it doesn't get the agents in the campaign_agents table who haven't done anything yet and are therefore not in the events table. Shouldn't a LEFT OUTER JOIN include everything in the first table, campaign_agents, even if there are no matches to the second table? Do I need to put and OR statement after the where to somehow get them included?
You can try:
SELECT agent.agentid
FROM agent
INNER JOIN (SELECT count(eventid) events, agentid
FROM event
WHERE timestamp BETWEEN /* (now - 24 hours) */ AND /* now */ -- adapt this
GROUP BY agentid
) last_24_hours
ON last_24_hours.agentid = agent.agentid
INNER JOIN (SELECT count(eventid) events, agentid
FROM event
WHERE timestamp BETWEEN /* (now - 10 minutes) */ AND /* now */ -- adapt this
GROUP BY agentid
) last_10_mins
ON last_10_mins.agentid = agent.agentid
WHERE last_24_hours.events < 400
AND last_10_mins.events < 10

Mysql: Adding product restricted shipping options to cart

I have a custom shop, and I need to redo the shipping. However, that is sometimes later, and in the meantime, I need to add a shipping option for when a cart only contains a certain range of products.
SO there is a ship_method table
id menuname name zone maxweight
1 UK Standard ukfirst 1 2000
2 UK Economy uksecond 1 750
3 Worldwide Air world_air 4 2000
To this I have added another column prod_restrict which is 0 for the existing ones, and 1 for the restricted ones, and a new table called ship_prod_restrict which contains two columns, ship_method_id and item_id, listing what products are allowed in a shipping category.
So all I need to do is look in my transactions, and for each cart, just check which shipping methods are either prod_restrict of 0 or have 1 and have no products in the cart that aren't in the restriction table.
Unfortunately it seems that because you can't values from an outer query to an inner one, I can't find a neat way of doing it. (edited to show the full query due to comments below)
select ship_method.* from ship_method, ship_prod_restrict where
ship_method.`zone` = 1 and prod_restrict='0' or
(
prod_restrict='1'
and ship_method.id = ship_prod_restrict.ship_method_id
and (
select count(*) from (
select transactions.item from transactions
LEFT JOIN ship_prod_restrict
on ship_prod_restrict.item_id = transactions.item
and ship_prod_restrict.ship_method_id=XXXXX
where transactions.session='shoppingcartsessionid'
and item_id is null
) as non_permitted_items < 1 )
group by ship_method.id
gives you a list of whether the section matches or not, and works as an inner query but I can't get that ship_method_id in there (at XXXXX).
Is there a simple way of doing this, or am I going about it the wrong way? I can't currently change the primary shipping table, as this is already in place for now, but the other bits can change. I could also do it within PHP but you know, that seems like cheating!
Not sure how the count is important, but this might be a bit lighter - hard to tell without a full table schema dump:
SELECT COUNT(t.item) FROM transactions t
INNER JOIN ship_prod_restrict r
ON r.item_id = t.item
WHERE t.session = 'foo'
AND r.ship_method_id IN (**restricted, id's, here**)

MySQL multiple left join group by

There is something wrong with this MySQL code.
it seems to be returning more stock then there should be.
table positions holds the stock available (multiple positions one product)
table orderbody holds the orders ordered products (1 orderheader to many orderbody)
SELECT PRO.ProductID,
PRO.ProductCode,
SUM( POS.Qty ) AS instock,
SUM( OB.Qty ) AS onorder
FROM products AS PRO
LEFT JOIN position AS POS ON POS.ProductID = PRO.ProductID
LEFT JOIN orderbody AS OB ON OB.ProductID = PRO.ProductID
WHERE POS.ProductID = OB.ProductID
GROUP BY PRO.ProductID, POS.ProductID, OB.ProductID
i'm getting instock 320
actual stock quantity = 40
number of positions = 2 (qty 20 each)
onorder = 16 qty
actual number of orderbody = 8 rows
actually on order = 8 (each with qty = 1)
this is on one of the products
i know it has something to do with the group by but i cant work it out.
Appreciate any help received.
I had the same problem a few days ago. Try it by SELECTing from a separate query: SELECT ... FROM products, (SELECT...)..." where you have the two tables to be left joined. try to test the sub-query by itself first, and try to put it together once ot works. (once you have the data you want, and not duplicates, because that is you problem.
you are selecting this field PRO.ProductCode, but not grouping by it, at a guess it might be the problem.