I asked a similar question yesterday which may have been poorly worded, either way I didn't understand and this is something I really need to crack :) I've never done it before and would be very useful for so many of my projects.
This is for a directory website. I have three tables: entry, location, and entry-locations. entry contains information about a building such as name, address, image, etc. location is simply a list of possible locations each building could be. The location table is pretty much irrelevant for this example, it just contains information about the location which I could display on other areas of the site.
entry-locations is a table which links the entries to the locations. It only has two fields, entry-id and location... If you're wondering why I need a seperate table for this is because the same building could have multiple locations (don't ask).
Basically, what I need to do is display listings from each location it's own page. For example, I need to list every building in France, so the query needs to go through the entry-locations table returning every record with the location 'France', then it needs to pull all the data from the entry table corresponding to the entry-id's returned.
I'm sure there is a way to do this with one query and would be extremely greatful if I could be shown how, I could replicate this in so many projects.
How about this one?
-- // Selects all the columns from both entry-locations and entry
SELECT *
FROM entry-locations
JOIN entry e ON e.id = el.entry-id
WHERE el.location = 'France';
-- // To just get the entry data for matching records: (Remove DISTINCT if you
-- // don't mind, or want, each entry with multiple locations in the result set
-- // multiple times)
SELECT DISTINCT e.*
FROM entry-locations el
JOIN entry e ON e.id = el.entry-id
WHERE el.location = 'France'
Edit: Ok, so I tried removing the location table, and instead used entry-location.location as the name of the location.. Is this correct?
Imagine you have this data:
Entry:
|id|name|
| 1|Foo |
| 2|Bar |
Entry-Location:
|entry-id|location|
|1 |France |
|2 |Greece |
|2 |France |
This is how I understand the tables from your description. A more common approach is to have
Entry(id,name)
Location(id,name)
Entry_Location(entry_id, location_id)
This is also the source of some of the confusions in the other posts, I think.
Now, ask MySql to fetch data from both tables, where the id's match up.
SELECT entry.*
FROM `entry`, `entry-location` as el
WHERE entry.id = el.`entry-id`
AND el.location = 'France';
MySql now treats your data like one table, looking like this:
|entry.id|entry.name|el.location|
| 1| Foo| France|
| 1| Foo| Greece|
| 2| Bar| France|
And from that table it selects the entries where el.location = 'France', and returns the specified fields.
This query fetches all the fields from the entry table that matches the requirements you set.
First it makes MySql think of the two tables as one table, by SELECT-ing from both of them.
Have a look at MySql's SELECT reference.
select * from entry where id in (select entry-id from entry-locations)
If I got your idea, you need:
select e.* frmm entry join entry-locations as as l ON l.entry-id=e.id WHERE l.location='France'
Related
I have a games database and I need to create pairs of similar games in another table. So, let's say, game ID 5 and ID 12 need to be paired, which should be like this:
---------------------------
| PairID | First | Second |
---------------------------
| 1 | 5 | 12 |
---------------------------
So what I need basically is to join pairs and games tables and select and display data for all IDs of each pair depending on which game is being browsed. That is, if it's game ID 5 then ID 12 should be displayed and vice versa. Seems pretty trivial at first, except I discovered there is no neareast elegant solution to this, where the major problem is the order of IDs in the pairs table.
First of all, with this scheme I'm forced to use two separate joins of the 'games' table and select both games' data regardless like this:
SELECT simpairs.id, simpairs.first, simpairs.second,
games_one.id AS first_id, games_two.id AS sec_id,
games_one.title AS first_title, games_two.title AS sec_title,
games_one.year AS first_year, games_two.year AS sec_year
FROM simpairs
LEFT JOIN games AS games_one ON simpairs.first = games_one.id
LEFT JOIN games AS games_two ON simpairs.second = games_two.id
WHERE simpairs.first = <id> OR simpairs.second = <id>
Secondly, I need the script to pick out and display the correct data like this:
$id = $_GET['id'];
while($row = mysqli_fetch_assoc($res)) {
if($row['first_id'] != $id) {
$game_id = $row['first_id'];
$title = $row['first_title'];
$year = $row['first_year'];
} else {
$game_id = $row['sec_id'];
$title = $row['sec_title'];
$year = $row['sec_year'];
}
...
}
Ok, so this is already pretty messy. Still, the biggest issue is sorting the output alphabetically. Obviously, it can't be easily done on the SQL side unless the query is somehow rebuilt for which there is no apparent route as it must include an equivalent for PHP condition, or the pairs table should be modified to contain a mirrored pair for each new entry like this:
---------------------------
| PairID | First | Second |
---------------------------
| 1 | 5 | 12 |
---------------------------
| 2 | 12 | 5 |
---------------------------
This would work of course and I would only need 1 join instead of 2, which would also make sorting easier directly on the SQL side, yet this also doesn't seem very elegant as I would have to perform two inserts for each new pair instead of just 1 plus clutter the database with mirrored entries making it exactly 2 times larger. I mean, if this is the right way then I guess I'm fine with that.
Sorting data on the script side is no less daunting. Typically, arrays are used for the occasion, except this time around I don't see how it can be achieved effectively. I will need to store ID, game title and year of release somehow and then sort everything based on titles alone. Meaning, I should be probably using 2 dimensional arrays where titles act as parent keys for sub-arrays containing IDs and years, then I should somehow sort only the parent keys without affecting the sub-arrays and I really don't know how to do that nor if it's worth it at all. All in all, seems like unnecessary strain to both the database and the script.
So what would be the optimal solution here?
You have an id that relates to a game in the games table.
You have a table that links other game id's to the main game id
You want to show a list of those linked games and related info.
Select lg.*
FROM `games` g
LEFT JOIN `simpairs` s
ON s.first = g.id OR s.second = g.id
JOIN `games` lg // linked games
on lg.id = case when s.first = g.id then s.second else s.first end
where g.id = ?
Let me know if you have any questions, it's pretty self explanatory. Though it does assume simpairs never has reverse duplicate values in first and second otherwise you might need to filter those out.
I have this very specific problem which I can't even decide how to approach. So I have 3 tables in MySQL.
Table recipe: id_recipe| name | text | picture
Table ingredients_recipe: id_rs | id_recipe| id_ingredients
Table ingredients: id_ingredient | name | picutre
This is a site, where you select ingredients(so the input is 1 or more id_ingredient) and it should display three categories:
All recipes you can make right now (you have all the ingredients required for it)
All recipes where you are missing only 1 or 2 ingredients
All recipes where you are missing only 3 or 4 ingredients.
Can you help me with these 3 SQL selects? I'm pretty deadlocked right now. Thanks.
SAMPLE DATA: http://pastebin.com/aTC5kQJi
I think your basic statement is already on the right track. You just need to do a little trick. You cannot compare them directly, but you can compare the count of ingredients:
SELECT id_receipe, count(id_rs) as ingredient_count
FROM ingredients_recipe
WHERE id_ingredient IN ( 2, 5)
GROUP BY id_recipe
This will give you the count of ingredients you have for each receipe. Now get the total amount of ingredients for each receipe
SELECT id_receipe, count(id_rs) as ingredient_count
FROM ingredients_recipe
GROUP BY id_recipe
an compare them. Taking the first query as a basis. You can easily get your three different categories out of this.
The question is not new in any way but it has a small twist to it.
My webpage is a membership page where users places bets. My idea is to create a new table for the users(with a naming convention like TABLE userBet+$userid) bets. User login information is already handled, my goal is now to save the bets of the user to a new table. A table which is created when users register. This will hopefully make score counting easier. Am I right or wrong? Could this be done in a better way? (Everything is done in PHP MySQL)
User registers -> Table for bets get created
"CREATE Table $userID ,id_bet, games, result, points"
And then matching this table against the correct result?
So again my questions: Is this a good way to do it? Is creating a table with the userID a smart thing to do?
EDIT
The bets is always 40 matches, which makes the tables Huge with columns and rows.
Should I make 40 Tables, one for each games instead? and put all users in there?
Am I right or wrong?
You are wrong. Dynamically altering your database schema will only make it harder to work with. There's no advantage you gain from doing so. You can do the same things by storing all bets within the same table, adding a column userid.
Posting as an answer due to author's request : )
Suggested database schema:
table matches:
id | name |
---------------
1 | A vs B |
table user_bets
id | user_id | match_id | points | result |
-------------------------------------------
1 | X | 1 | Y | Z |
Where match_id is related on matches.id
user_id = user.id
user_bets is only one table, containing all the info. No need of separate tables, as it was clear from the comments it's considered bad practice to alter the db schema via user input.
Trying to write statement where in single statement select all (*) and sum one column from the same database and the same table, depending on conditions.
Wrote such statement (based on this Multiple select statements in Single query)
SELECT ( SELECT SUM(Amount) FROM 2_1_journal), ( SELECT * FROM 2_1_journal WHERE TransactionPartnerName = ? )
I understand that SELECT SUM(Amount) FROM 2_1_journal will sum all values in column Amount (not based on codition).
But at first want to understand what is correct statement
With above statement get error SQLSTATE[21000]: Cardinality violation: 1241 Operand should contain 1 column(s)
Can not understand error message. From advice here MySQL - Operand should contain 1 column(s) understand that subquery SELECT * FROM 2_1_journal WHERE TransactionPartnerName = ? must select only one column?
Tried to change statement to this SELECT ( SELECT * FROM 2_1_journal WHERE TransactionPartnerName = ? ), ( SELECT SUM(Amount) FROM 2_1_journal), but get the same error...
What would be correct statement?
SELECT *, (SELECT SUM(Amount) FROM 2_1_journal)
FROM 2_1_journal
WHERE TransactionPartnerName = ?
This selects sums up Amount from the entire table and "appends" all rows where TransactionPartnerName is the parameter you bind in the client code.
If you want to limit the sum to the same criteria as the rows you select, just include it:
SELECT *, (SELECT SUM(Amount) FROM 2_1_journal WHERE TransactionPartnerName = ?)
FROM 2_1_journal
WHERE TransactionPartnerName = ?
A whole different thing: table names like 2_1_journal are strong indicators of a broken database design. If you can redo it, you should look into how to normalize the database properly. It is most likely pay back many times over.
With regard to normalization (added later):
Since the current design uses keys in table names (such as the 2 and 1 in 2_1_journal), I'll quickly illustrate how I think you can vastly improve that design. Lets say that the table 2_1_journal has the following data (I'm just guessing here because the tables haven't been described anywhere yet):
title | posted | content
------+------------+-----------------
Yes! | 2013-01-01 | This is just a test
2nd | 2013-01-02 | Another test
This stuff belongs to user 2 in company 1. But hey! If you look at the rows, the fact that this data belongs to user 2 in company 1 is nowhere to be found.
The problem is that this design violates one of the most basic principles of database design: don't use keys in object (here: table) names. A clear indication that something is very wrong is if you have to create new tables if something new is added. In this case, adding a new user or a new company requires adding new tables.
This issue is easilly fixed. Create one table named journal. Next, use the same columns, but add another two:
company | user | title | posted | content
--------+------+-------+------------+-----------------
1 | 2 | Yes! | 2013-01-01 | This is just a test
1 | 2 | 2nd | 2013-01-02 | Another test
Doing it like this means:
You never add or modify tables unless the application changes.
Doing joins across companies or users (and anything else that used to be part of the table naming scheme is now possible with a single, fairly simple select statement).
Enforcing integrity is easy - if you upgrade the application and want to change the tables, the changes doesn't have to be repeated for each company and user. More importantly, this lowers the risk of having the application get out of sync with the tables in the database (such as adding the field comments to all x_y_journal tables, but forgetting 5313_4324_journal causing the application to break only when user 5313 logs in. This is the kind of problem you don't want to deal with.
I am not writing this because it is a matter of personal taste. Databases are just designed to handle tables that are laid out as I describe above. The design where you use object keys as part of table names has a host of other problems associated with it that are very hard to deal with.
I am quite new to PHP and MySQL, but have experience of VBA and C++. In short, I am trying to count the occurrences of a value (text string), which can appear in 11 columns in my table.
I think I will need to populate a single-dimensional array from this table, but the table has 14 columns (named 'player1' to 'player14'). I want each of these 'players' to be entered into the one-dimensional array (if not NULL), before proceeding to the next row.
I know there is the SELECT DISTINCT statement in MySQL, but can I use this to count distinct occurrences across 14 columns?
For background, I am building a football results database, where player1 to player14 are the starting 11 (and 3 subs), and my PHP code will count the number of times a player has made an appearance.
Thanks for all your help!
Matt.
Rethink your database schema. Try this:
Table players:
player_id
name
Table games:
game_id
Table appearances:
appearance_id
player_id
game_id
This reduces the amount of duplicate data. Read up on normalization. It allows you to do a simple select count(*) from appearances inner join players on player_id where name='Joe Schmoe'
First of all, the database schema you're using is terrible, and you just found out a reason why.
That being said, I see no other way then to first get a list of all players by distinctly selecting the names of players into an array. Before each insertion, you would have to check if the name is already in the array (if it is already in, don't add it again).
Then, when you have the list of names, you would have to run an SQL statement for each player, adding up the number of occurences, like so:
SELECT COUNT(*)
FROM <Table>
WHERE player1=? OR player2=? OR player3=? OR ... OR player14 = ?
That is all pretty complicated, and as I said, you should really change your database schema.
This sounds like a job for fetch_assoc (http://php.net/manual/de/mysqli-result.fetch-assoc.php).
If you use mysqli, you would get each row as an associative array.
On the other hand the table design seems a bit flawed, as suggested before.
If you had on table team with team name and what not and one table player with player names.
TEAM
| id | name | founded | foo |
PLAYER
| id | team_id | name | bar |
With that structure you could add 14 players, which point at the same team and by joining the two tables, extract the players that match your search.