SQL statement greater issue - mysql

I'm building a small gym training system and I'm currently having a problem with an SQL query. I need to get the names of all customers who are more than ten kilos heavier than their target weight. Here are my tables.
Person: PersonID | Name | DOB | Email | Gender
Item: ItemID | Name | Portion | kCal | Fat
Training: TrainingID | Person_ID | StartDate | StartWeight | TargetWeight | CurrentWeight
Now I can get the list of people who are heavier than their target weight, but I specifically need to receive customers who are more than 10 kg heavier than their target weight. I know it's probably basic but I just couldn't get my head around it for somehow. Any help would be appreciated.
Thank you

select p.*
from person p
join training t on t.person_id = p.personid
where t.currentweight - t.targetweight > 10

Related

Left outer join with multiple matches, how to return a specific one?

I have a problem which I think might be solved with proper use of left outer join, but I'm unable to construct suitable query. OTOH, there may also be some other, more clever solution with SQL. In addition, this could easily be solved with some programming, but I want to avoid that and find as "clean" solution as possible.
Background: let's say I'm creating a website that lists some car brands and the user can select which ones he owns/has owned (I'm not really doing that, but this example illustrates the point). In addition, for the selected ones he can optionally enter some additional info about them, e.g. year and some free text like specific model, comments or whatever. In addition, the information entered is stored in a relational database (MySQL in my case) and the user can retrieve and change his answers later.
Let's say there are two database tables:
BRAND
------------
ID INT
NAME VARCHAR(50)
OWNED
------------
ID INT
BRAND_ID INT
OWNER_ID INT
YEAR INT
COMMENT VARCHAR(100)
(here BRAND_ID + OWNER_ID is an unique index, so there can be only one row, and thus one year & comment for each BRAND/OWNER combination)
The data in these tables may look something like this:
BRAND
--------------
ID | NAME
--------------
1 | Cadillac
2 | Chevrolet
3 | Dodge
4 | Ford
OWNED
-----------------------------------------
ID | BRAND_ID | OWNER_ID | YEAR | COMMENT
-----------------------------------------
1 | 1 | 1 | null | 70's Fleetwood
2 | 2 | 1 | 2000 | Crappy car
3 | 2 | 2 | null | I really liked it
4 | 4 | 2 | 1999 | null
Now, to facilitate easy creation of the web page, what I would like to do is to with one SELECT display all brands in table BRAND, and for each BRAND to know whether current user has owned it or not, and if he has, also list his year and comment (if any). In other words, something like this (assuming current user is 2):
NAME | OWNER_ID | YEAR | COMMENT
-------------------------------------
Cadillac | null | null | null
Chevrolet | 2 | null | I really liked it
Dodge | null | null | null
Ford | 2 | 1999 | null
I tried doing something like:
select NAME, OWNER_ID, YEAR, COMMENT from BRAND left join OWNED on
BRAND.ID = OWNED.BRAND_ID where OWNER_ID = 2 or OWNER_ID = null
but that fails because 1 owns a Cadillac and thus Cadillac is left from the result. OTOH if I omit the where clause, I will get two rows for Chevrolet, which is also not desirable.
So, if there is a clean solution with SQL (either with or without left outer join), I'd like to know how to do it?
I am guessing you want this:
select NAME, OWNER_ID, YEAR, COMMENT
from BRAND left join
OWNED o
on BRAND.ID = OWNED.BRAND_ID and OWNER_ID = 2 ;
Seems like what you might actually want is a list of the owners, followed by what they owned and the details. You can that by adjusting the owner id at the bottom of this one:
SELECT owned.owner_id, brand.id, brand.name, owned.year, owned.comment
FROM owned
INNER JOIN brand
ON owned.brand_id = brand.id
WHERE owned.owner_id = 2
Tested here: http://sqlfiddle.com/#!9/5b52ba

How to formulate query to show all courses taken by a person

I'm having trouble formulating a MySQL query correctly. Everything I've tried doesn't give me what's needed, or gives a syntax error.
I have three tables: Clients, Courses, and CoursesForClients.
The Clients table just has basic coordinates for a person: ID, Name, Address, email, etc.
+----------+-----------------------------+------+
| ClientID | Name | Address | etc. |
+----------+-----------------------------+------+
| 10 | Joe Smith | 1 Main St. | ... |
| 20 | Bob Smith | 2 Main St. | ... |
| ... | ... ... | ... ... ... | ... |
+----------+-----------------------------+------+
The Courses table stores the course name and its ID.
+----------+-----------------------+
| CourseID | Name |
+----------+-----------------------+
| 100 | Intro. to Subject |
| 200 | Intermediate Subject |
| 300 | Advanced Subject |
| ... | ... ... ... ... |
+----------+-----------------------+
The CoursesForClients table has the CourseID and ClientID. A given Client can have taken multiple courses, so for every course that a Client has taken, there's a row, with the person's ID and the Course ID.
+----------+----------+
| CourseID | ClientID |
+----------+----------+
| 100 | 1 |
| 200 | 1 |
| 300 | 1 |
| 100 | 2 |
| 200 | 2 |
| ... | ... |
+----------+----------+
Now, what I need is to be able to list the Client - just once - together with all the Courses she has taken. So, the result of the query might look like this:
10:Joe Smith
1 Main St.
Somewhere, AL
Intro. to Subject
Intermediate Subject
Advanced Subject
---------------------------
20:Bob Smith
2 Main St.
Somewhere, AL
Intro. to Subject
Intermediate Subject
So this output reflects the relationships between the Client and the Course. The key thing here is that, no matter how many Courses a Client has taken, the Client's particulars appear only once, followed by the list of all the courses she's taken.
There's an additional twist in that there's another table that lists the Grade for the Course for the Client, and that GradeID is also stored in the CoursesForClients table, and there's another table of Grades, with ID and Grade Description. But I won't worry about this right now. For now, all I want is just the basic output shown, as described above.
It looks like it should be easy to set up a query for this, with a JOIN and maybe a GROUP BY, but I'm having a block here and can't seem to get it right. So, any help will be hugely appreciated. Thank you!
SQL deals in tables. By definition a table has a bunch of rows, each of which has the same columns as each other. Your query is going to yield a result set that duplicates the client's information for each course she took.
Your presentation layer is going to format that table, by noticing the first row of each new client and breaking out the client header. You'll do that in php or Java or Crystal Reports or some such presentation tech.
Your query is something like this.
SELECT a.id, a.name, a.address, a.etc,
c.Name
FROM Clients a
JOIN CoursesForClients b USING(ClientID)
JOIN Courses c USING(CourseID)
ORDER BY a.id, c.CourseID
#Strawberry makes a good point about the pitfall of using USING(). Here is the same query on ON.
SELECT a.id, a.name, a.address, a.etc,
c.Name
FROM Clients a
JOIN CoursesForClients b ON a.ClientID = b.ClientID
JOIN Courses c ON b.CourseID = c.CourseID
ORDER BY a.id, c.CourseID

One-to-one relation through pivot table

Okay so I have a soccer website im building when a user signs up they get a team and and 6 different stadium to chose from. so I have a teams table:
----------------------------------------
| user_id | team_name | stadium_id |
----------------------------------------
| 1 | barca | 2 |
----------------------------------------
Then I decided to make the stadiums its own table
----------------------------------------------
| id | name | capacity | price |
----------------------------------------------
| 1 | Maracana | 50000 | 90000 |
------------------------------------------------
| 2 | Morombi | 80000 | 150000 |
------------------------------------------------
to get the teams arena name I would have to get the arena_id from the teams table and then fetch the arena name with the id. Now I don't think this is very efficient so I gave it some thought and I think the best solution is adding a pivot table like so:
| id | arena_id | team_id |
---------------------- ----------------
| 1 | 2 | 1
--------------------------------------|
| 2 | 1 | 2
--------------------------------------|
I usually think of pivot tables as tables for many to many relationships not one to one relationships. Is using a pivot table in this instance the best solution or should I just leave the implementation I'm currently using?
You don't need to use a pivot-table for this. It can either be a One-To-One or a One-To-Many relationship. It's a One-To-One if every user/team does only relate to one stadium (no stadium can be used by two teams). In a One-To-Many relationship multiple teams/users could use the same stadium, which might become necessary if you have thousands of users and start running out of stadiums.
A JOIN statement would be efficient and sufficient here.
SELECT s.name, t.team_name -- Get the team's and stadium's name
FROM team t -- From the team table
JOIN stadium s -- Join it with the stadium table
ON (t.stadium_id = s.id) -- Join ON the stadium_id
This will return the team name and stadium name of every registered team.
You might need to adjust the query, but you should be able to catch the grasp of it after reading the MySQL reference I linked above.

MySQL Query for 4 different tables

I apologise in advance if this might seem simple as my assignment needs to be passed in 2 hours time and I don't have enough time to do some further research as I have another pending assignment to be submitted tonight. I only know the basic MYSQL commands and not these types. And this is one of the final questions left unanswered and is making me go nuts even if i have already read the JOIN documentation . Please help.
Say I have 4 tables
_______________ _______________ _______________ _______________
| customers | | orders | | ordered_items | | products |
|_______________| |_______________| |_______________| |_______________|
|(pk)customer_id| | (pk)order_id | | (pk)id | |(pk)product_id |
| first_name | |(fk)customer_id| | (fk)order_id | | name |
| last_name | | date | |(fk)product_id | | description |
| contact_no | | | | quantity | | price |
|_______________| |_______________| |_______________| |_______________|
How would i be able to query all the products ordered by (eg: customer_id = '5')
I only know basic SQL like straight forward queries on 1 table and joins from 2 different, but since its 4 different tables having different relations to one another, how would i be able to get all the products ordered by a particular customer id?
Because its like get all the products from ordered products where order_id = (* orders by customer_id = 5).
But what can be an optimised and best practice way in doing this type of query
You only need to join 3 tables - orders, order_items, and products:
SELECT DISTINCT products.*
FROM products
JOIN order_items USING (product_id)
JOIN orders USING (order_id)
WHERE orders.customer_id = 35
As many have mentioned, you would do yourself a big favor by learning about table JOINS. There isn't much difference in the syntax between joining 2 tables to joining 4 or more.
SQLFiddle is a highly recommended resource for practicing and sharing your queries.
This is a comment because you appear to be new to SQL. You need to learn basic syntax for queries (which is why you are getting downvoted).
But you also ask about form. The data structure is actually pretty well laid out. I do have two comments. First, you should be consistent about how you name the id columns. For Ordered_Items, the id should be ordered_item_id.
Second, you should avoid using SQL special words for columns names and table names. Instead of date, use OrderDate.

Continue most recent value over a time range

I have this existing schema where a "schedule" table looks like this (very simplified).
CREATE TABLE schedule (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(45),
start_date date,
availability int(3),
PRIMARY KEY (id)
);
For each person it specifies a start date and percentage of work time available to spent on this project. That availability percentage implicitly continues until a newer value is specified.
For example take a project that lasts from 2012-02-27 to 2012-03-02:
id | name | start_date | availability
-------------------------------------
1 | Tom | 2012-02-27 | 100
2 | Tom | 2012-02-29 | 50
3 | Ben | 2012-03-01 | 80
So Tom starts on Feb., 27nd, full time, until Feb, 29th, from which on he'll be available only with 50% of his work time.
Ben only starts on March, 1st and only with 80% of his time.
Now the goal is to "normalize" this sparse data, so that there is a result row for each person for each day with the availability coming from the last specified day:
name | start_date | availability
--------------------------------
Tom | 2012-02-27 | 100
Tom | 2012-02-28 | 100
Tom | 2012-02-29 | 50
Tom | 2012-03-01 | 50
Tom | 2012-03-02 | 50
Ben | 2012-02-27 | 0
Ben | 2012-02-28 | 0
Ben | 2012-02-29 | 0
Ben | 2012-03-01 | 80
Ben | 2012-03-02 | 80
Think a chart showing the availability of each person over time, or calculating the "resource" values in a burndown diagram.
I can easily do this with procedural code in the app layer, but would prefer a nicer, faster solution.
To make this remotely effective, I recommend creating a calendar table. One that contains each and every date of interest. You then use that as a template on which to join your data.
Equally, things improve further if you have person table to act as the template for the name dimension of your results.
You can then use a correlated sub-query in your join, to pick which record in Schedule matches the calendar, person template you have created.
SELECT
*
FROM
calendar
CROSS JOIN
person
LEFT JOIN
schedule
ON schedule.name = person.name
AND schedule.start_date = (SELECT MAX(start_date)
FROM schedule
WHERE name = person.name
AND start_date <= calendar.date)
WHERE
calendar.date >= <yourStartDate>
AND calendar.date <= <yourEndDate>
etc
Often, however, it is more efficient to deal with it in one of two other ways...
Don't allow gaps in the data in the first place. Have a nightly batch process, or some other business logic that ensures all relevant dat apoints are populated.
Or deal with it in your client. Return each dimension in you report (data, and name) as seperate data sets to act as your templates, and then return the data as your final data set. Your client can itterate over the data and fill in the blanks as appropriate. It's more code, but can actually use less resource overall than trying to fill-the-gaps with SQL.
(If your client side code does this slowly, post another question examining that code. Provided that the data is sorted, this is acutally quite quick to do in most languages.)