I'm trying to set up a high scores board for a game I'm making. On the board, I'm using foreign keys to two other boards, players and weapons. Each score stores the four weapons the player used on that run. The tables are set up like this:
Scores
id|playerid|score|weapon0id|weapon1id|weapon2id|weapon3id
Players
id|name
Weapons
id|name
I want to select multiple rows from the scores table with ids replaced by the appropriate names. I'm able to get the correct player name and one weapon using this statement:
SELECT scoreID, Players.playerName, scoreVal,
Weapons.weaponLabel, scoreW1, scoreW2, scoreW3
FROM Scores, Players, Weapons
WHERE Players.playerID = scorePlayer AND Weapons.weaponID = scoreW0
Everywhere I've looked shows that to be the best way to get a value from a row referred to by a foreign key. It works fine for the player name, but there seems to be no way to expand this to fill in multiple weapon names at once. Using an OR with the remaining weapons or using weaponID IN (w0,w1,w2,w3) seems to get one row for each weapon, not one row with each weapon in the appropriate spot.
Is there any way to get the correct weapon names just using the select statement? Or will I need to have extra code loop through and replace each weapon id with the correct name?
This design is questionable: weapon0..n will likely lead to nothing but difficult queries like this. The queries will also have to be de-normalized - e.g. one join per weapon0..n.
Anyway, the query is wrong and will return many more rows than desired because it uses the form FROM a,b which implies a CROSS JOIN between a and b and there is not appropriate selectors in the WHERE to make it an equi-join. Try to use a normal (INNER) JOIN and ON to make each join more apparent:
SELECT s.scoreID, p.playerName, s.scoreVal,
w0.weaponLabel as w0Label,
w1.weaponLabel as w1Label
-- etc
FROM Scores s
JOIN Players p ON p.id = s.playerID
JOIN Weapons w0 ON w0.weaponID = s.scoreW0
JOIN Weapons w1 ON w1.weaponID = s.scoreW1
-- etc, ick!!!
By now it should become apparent why the de-normalized data is icky!
Each column must be joined with a different relation (w0, w1, etc).
I usually have to create a looping procedure to get all the denormalized columns in one row per unique set, in your case player, weaponlabel.
Related
I have one student table links to two scores tables which are exactly the same structures. The only different is that one table will stores high scores, the other table stores low scores. Now I need to query both high and low score tables, it will list all subjects scores with student name if it is from high score table and scores with name is student if its from low score table, and I need to order the result by time.
SELECT u.student_name,
a.subject1_score,
a.subject2_score,
a.subject3_score,
a.subject4_score,
a.subject5_score,
a.exam_date
FROM Student u
INNER JOIN High_Score_Table a
On u.student_id = a.student_id
ORDER BY a.exame_date = time
Then for low_score_Table I will have almost same query except that the student name will equal to Student by default.
Then I will need to put this together in a list and order by time. How could I do that shorter and better ?
Btw, I can merge two tables low and high_score into one, and add a column called "flag" into that, whenever the flag value equal to "show" then I show student name with all score records, else "hidden" I will just show "Student" and all score records. How could I do that in one query ?
It sounds like you need a UNION, because you are concatenating two distinct result sets - one from High_Score_Table and one from (presumably) Low_Score_Table:
select s.student_name,
h.subject1_score,
h.subject2_score,
h.subject3_score,
h.subject4_score,
h.subject5_score,
h.exam_date
from High_Score_Table h
join Student s on h.student_id = u.student_id
union all
select 'student' as student_name,
l.subject1_score,
l.subject2_score,
l.subject3_score,
l.subject4_score,
l.subject5_score,
l.exam_date
from Low_Score_Table l
order by exam_date
The takeaway here is an ORDER BY clause in a union sorts over the entire result set - which is in this case exactly what you want.
Prerequisites
I have two tables. A list of people in one table, and how they prefer each other in a foreign key lookup table. The first table is only the list of people. The other is where they all list a few other people they would prefer to have as a roommate.
Table People:
List of people with ID, name and surname, etc
Table Choices:
List of choosers (FK People ID)
List of chosen ones (FK People ID)
Question
How can I list matches with SQL (or PHP)? That is, where one person is also on the list on the person he wanted to have as a roommate? Basically you have a chooser with a list of chosen ones. How would you check if the chooser is also on the list of one of his or her chosen ones?
Basically I want a report with every stable match, that is where the chooser is also on the list of at least one of his or her chosen ones.
I am guessing a for loop would do the trick, but how would you even put together the first iteration? Much less the rest of the loop?
Join based solution:
SELECT
r1.name as name1,
r2.name as name2
FROM
roommate r1
JOIN
roommate_pair rp1 ON r1.id = rp1.chooser_id
JOIN
roommate r2 ON r2.id = rp1.choosen_id
JOIN
roommate_pair rp2 ON r2.id = rp2.chooser_id
WHERE
rp2.choosen_id = r1.id
GROUP BY
CONCAT(GREATEST(r1.id,r2.id),'-',LEAST(r1.id,r2.id))
Last GROUP BY is to remove duplicate matches in swapped columns. Working SQL Fiddle
SELECT a.chooser, a.chosen
FROM roommates a,roommates b
WHERE a.chooser = b.chosen
AND a.chosen = b.chooser;
Using the above query you should get the cross-referenced id's... You do, however, get doubles (both references are returned). See SQL Fiddle.
You could do a check on that in your PHP-code.
This piece of code should provide you some hint. First you will iterate through all the people. Then from the list of possible preferred people, you select only those who, in turn, have the original person in their list of preferred people.
for cc in (select * from people) loop
for dd in (select * from preferences pr where pr.source_id = cc.people_id and exists (select 1 from preferences pr1 where pr1.source_id = pr.friend_id and pr1.friend_id = cc.people_id)) loop
--do your stuff here
end loop
end loop
I have two tables:
One is called data and there is only one (unique) row per ID.
Second is called images and there are 3 rows per ID.
Every time the page loads i would like to fetch data and one image for exactly 5 different IDs.
My question now is: Two separate SELECT queries or one query where both are joined.
Queries:
...
$all = $row["iD"] // includes **5** last iDs - fetched from DB
$all = implode(',',$all);
SELECT Name, Address FROM data WHERE iD IN($all);
SELECT url FROM images WHERE iD IN ($all) LIMIT 1;
I already have 3 other select queries on page, so i would like to know what is best regarding performance, one bigger - joined or two small - faster queries.
If join, how would these two be joined?
You have three images per ID and desire one image per ID for the last inserted images (aka "recent content" )?
Then you could use one easy natural join combined with group by like this:
SELECT d.Name, d.Address, MAX(i.url)
FROM data d, images i
WHERE i.iD = d.iD
GROUP BY d.Name, d.Address
ORDER BY d.iD DESC
LIMIT 5
Most of the time it is better to combine selects to skip the programmitcally overhead (calling mysql_query() in an loop itself for example).
But sometimes it depends on the underlying data.
Since your queries go to completely separate tables, I recommend you stay with 2 separate queries: This keeps the result sets smaller and makes it more likely, that at least one stays in the query cache,
Concerning your 2nd query: Do you understand, that this is not guaranteed to fetch a special URL, but any? Mostly the first one by key, but not guaranteed so.
For an answer on performance issues, see JOIN queries vs multiple queries . What I can understand from there is that performance issues vary depending on the specific situation so you should test both.
For the join, you could do;
SELECT User.iD, User.Name, User.Address, Image.url
FROM images as Image
JOIN data as User
ON Image.iD = User.iD
WHERE Image.iD IN ($all)
LIMIT 1;
It is not tested yet, so you should take it with a grain of salt. It is at least a starting point.
I am building a chorus management database and need to create a particular query. (MySQL programming in PHP.)
I have a table of singers and a table of events which have a many-to-many relationship managed by a roster table. Each roster record links to one SingerID and one EventID. I would like to create a browse table of the form:
The catch is that some singers may have no events linked.
Is there a way to do this in a single MySQL query, or will I need to write one query to list all my singers, and a second query to list all of the events for each singer, and then examine the second query to extract the first and last records (assuming I sort the last query by date)?
Use a LEFT JOIN that way it doesn't need to exist
I also think the way Mike does, Query should be like this
SELECT * FROM singer
LEFT OUTER JOIN roster ON singer.id =roster.singerid
INNER JOIN event ON event.id=roster.eventid
ORDER BY singer.name,singer.date
Using MySQL + PHP, I want to store several food_items for a single restaurant order in a MySQL database.
I have two tables: orders & food_items:
Many food_item_ids will be stored for a single order.
I'm just wondering, what's the best approach to storing the food_item_ids in the orders_table?
Should I store each food_item_id in a separate row of the orders table, or place all of a particular order's food_item_ids into a single row?
If you want 3NF (and you should unless and until you find performance is a problem), you should either:
store the order ID in each row of the food_item table (if food_items is a per-order table);
or use a many-to-many relationship (if food_items is just the possible things you can order).
With the first option, something like this would suffice:
Orders:
order_id
other stuff
FoodItems:
order_id
item_id
other stuff.
For the second option, a separate table provides the many-to-many relationship:
Orders:
order_id
other stuff
FoodItems:
item_id
other stuff.
FoodItemsInOrder:
order_id
item_id
count
In my opinion, all database tables should be designed in third-normal form. Once you table gets so big that performance may become an issue (and it will have to be very big), then you can start thinking about de-normalizing for speed.
For a restaurant, I cannot imagine the order and food item tables getting anywhere near big enough to warrant de-normalizing.
If each food item is unique to a particular order, then you should store the order ID in the food_items table, then you could query like this:
SELECT orders.id, food.id
FROM orders
INNER JOIN food ON orders.id = food.order_id
However if you have standard food items, e.g. "burger", "chips", "hot dog" then you should use three tables. One for orders, one for food items, and one to join the two:
SELECT orders.id, food.id
FROM orders
INNER JOIN orders_food ON orders.id = orders_food.order_id
INNER JOIN food ON orders_food.food_id = food.id