I'm looping through records in a table, and in each loop it opens another table to pull data from it. I need to ORDER the main loop ($rs) by a field from the 2nd table.
I'm somewhat a beginner to php and sql so go easy on me :P
code example:
$sql_result = mysql_query("SELECT * FROM table1", $db); // i want this to ORDER BY data from table2
while($rs = mysql_fetch_array($sql_result)) {
$sql_result2 = mysql_query("SELECT * FROM table2 WHERE id='$rs[data]'", $db);
$rs2 = mysql_fetch_array($sql_result2);
//get data from table 2
Looping through a resultset and executing a second query for each row is bad practice and causes unnecessary load on the database.
It looks like you need an INNER JOIN:
SELECT t1.*
FROM table1 t1
INNER JOIN table2 t2 ON t2.id = t1.data
ORDER BY t2.field
This way, you can get all the data with one single query.
EDIT:
Generally you should try to avoid returning all columns (SELECT *) and select only the columns that you really need:
SELECT t1.id, t2.field1, t1.field2, ...
This will not only reduce the load on the database (less data to select and to transfer over the network), but it will also help to eliminate double column names because double column names cause problems when you want to display them (as you found out yourself).
You can do two things to avoid this problem:
if both tables have columns with identical names and identical content (e.g. if these columns are used to join the tables), just select one of them (no matter which one, because they are identical).
if both tables have columns with identical names and different content and you need both of them, the easiest way would be to give one of them an alias:
SELECT t1.id, t2.id AS AnotherId, ...
This will cause the second column to be named "AnotherId", so you can get it with $rs[AnotherId].
Honestly, I'm no PHP guru so I don't know if PHP understands $rs[table.field] as well, but there are enough data access technologies which have problems when a query returns two columns with identical names...so aliasing duplicate columns can never be wrong.
Related
I have two tables : items and comments.
I want to select all items which a user commented on.
For simplicity, lets assume the items table has two columns : item_id and item_content. Let the comments table have 3 columns user_id, item_id and comment_content.
I am given the user_id of the commenting user, I need to first select all the item_id from the comments table, where user_id = myUserId.
This is a basic query SELECT item_id FROM comments WHERE user_id = '$myUserId'.
Then I need to select the item_content for each item_id returned by the previous query.
I was thinking of doing a while($row = $my_first_query->fetch_array()) loop, and inside of it doing something like SELECT item_content FROM item WHERE item_id = $row["item_id"]
however this is a bit messy and I was wondering if there was a simpler way of doing this, by combining the two queries into one.
Use an INNER JOIN:
SELECT t1.*
FROM items t1
INNER JOIN comments t2
ON t1.item_id = t2.item_id
WHERE t2.user_id = myUserId
The approach you suggested of first querying the comments table and then looping over the result set is inefficient. In a join, MySQL can handle this algebra much faster than your PHP code.
Depending of what you want to do exactly, you can just use a JOIN clause. The "header" table info will be found within all rows, so it might not be what you want to do.
Another way would be to run two distinct queries, the first one unchanged and the second one with a join. You would then have one result with the header, and the other with all the details that you could go through. It's more performant than run the same query over and over network wise.
So, I am trying to select some data from 4 tables using a query I have attempted to throw together.
SELECT *
FROM cards
LEFT JOIN cards_viewers ON cards.card_id = cards_viewers.card_id
(SELECT *
FROM folders
WHERE folder_id = cards.card_folderID)
(SELECT user_firstName,
user_lastName,
user_avatar
FROM user_data
WHERE user_id = cards_viewers.user_id)
WHERE cards_viewers.user_id = '.$u_id.'
ORDER BY cards.card_lastUpdated DESC
Basically, the query selects data from the four tables depending on the user_id in table user_data. I have attempted to initially fetch all data from the tables cards, and cards_viewers, and have went on to use this data to select values from the other tables (user_data and folders).
The query is wrong, I know that. I have learnt the majority of basic MySQL, but I am still struggling with more complex queries like the one I am trying to write now. What query can I use to select the data I want?
Links to any documentation to parts of queries would prove very useful in helping me learn how to create queries in future, rather than just relying on StackOverflow.
Many thanks.
You don't need "MULTI-WHERE" but multiple joins, you just need to keep doing joins until you get the tables you need.
Here's an example:
SELECT *
FROM cards LEFT JOIN cards_viewers
ON cards.card_id = cards_viewers.card_id
LEFT JOIN folders
ON folders.folder_id = cards.card_folderID
LEFT JOIN user_data
ON user_id = cards_viewers.user_id
WHERE cards_viewers.user_id = '.$u_id.'
ORDER BY cards.card_lastUpdated DESC
To custom the fields you want to get just change * for the name of the field being careful about ambiguous column naming.
For further information check MySql Joins. Hope this helped you :)
What I want to do is to query three separate tables into one row which is identified by a unique reference. I don't really have full understanding of the Join clause as it seems to require some sort of related data from each table.
I know I can go about this the long way round, but can not afford to lose even a little efficiency. Any help would be greatly appreciated.
Table Structure
package_id int(8),
client_id int(8),
unique reference varchar (40)
Each of the tables have essentially the same structure. I just need to know how to query all three, for 1 row.
If you have few tables that are sharing the same or similar definition, you can use union or union all to treat them as one. This query will return rows from each table having requested reference. I've included OriginTable info in case your code will need to refer to original table for update or something else.
select 'TableA' OriginTable,
package_id,
client_id
from TableA
where reference = ?
union all
select 'TableB' OriginTable,
package_id,
client_id
from TableB
where reference = ?
union all
select 'TableC' OriginTable,
package_id,
client_id
from TableC
where reference = ?
You might extend select list with other columns, provided that they have the same data type, or are implicitly convertible to data type from first select.
Let's say you have 3 tables :
table1, table2 and table3 with structure
package_id int(8),
client_id int(8),
unique reference varchar (40)
Let's assume that column reference is unique key.
Then you can use this:
SELECT t1.exists_row ,t2.exists_row ,t3.exists_row FROM
(
(SELECT COUNT(1) as exists_row FROM table1 t1 WHERE
t1.reference = #reference ) t1,
(SELECT COUNT(1) as exists_row FROM table1 t2 WHERE
t2.reference = #reference ) t2,
(SELECT COUNT(1) as exists_row FROM table1 t3 WHERE
t3.reference = #reference ) t3
) a
;
Replace #reference with actual value of unique key
or when you provide output of
SHOW CREATE TABLE
I can rewrite SQL with actual query
It is entirely possible to create a join between tables using a where clause. In fact this is often what I do as I find it leads to clearer information of what you are actually doing, and if you don't get the results you expect you can debug it bit by bit.
That said however a join is certainly a lot quicker to write!
Please bear in mind I'm a bi rusty on SQL so I may have missed remembered, and I'm not going to include any code as you haven't said what DBMS you are using as they all have slightly different code.
The thing to remember is that the join functions on a column with the same data (and type) within it.
It is much easier if each table has the 'joining' field named the same, then it should be a matter of
join on <nameOfField>
However if you wish to use field that have different names in the different tables you will need to list the fully qualified names. ie tableName.FieldName
If you are having trouble with natural, inner and outer, left and right, you need to think of a venn diagram with the natural being the point of commonality between the tables. If you are using only 2 tables inner and outer are equivalent to left and right (with each table being a single circle in the venn diagram) and left and right being the order of the tables in your list in the main part of your select (the first being the left and the second being the right).
When you add a third table this is where you can select any of the cross over section using these keywords.
Again however I have always found it easier to do a primary select and create a temp table, then perform my next join using this temp table (so effectively only need to use natural or left and right again). Again I find this easier to debug.
The best thing is to experiment and see what you get in return. Without a diagram of your tables this is the best I can offer.
in brief...
nested selects where field = (select from table where field = )
and temp tables
are (I think) easier to debug... but do take more writting !
David.
array_of_tables[]; // contain name of each table
foreach(array_of_tables as $val)
{
$query="select * from `$val` where $condition "; // $conditon
$result=mysqli_query($connection,$query);
$result_row[]=mysqli_fetch_assoc($result); // if only one row going to return form each table
//check resulting array ,for your row
}
SELECT * FROM table1 t1 JOIN table2 t2 ON (t2.unique = t1.unique) JOIN table3 t3 ON (t3.unique = t1.unique) WHERE t1.unique = '?';
You could use a JOIN like this, assuming all three tables have the same unique column.
What is the best way to get labels of id's .
Here is the problem i'm facing .
I have a many tables that only contains id's (subject_id , level_id , place_id , etc..)
What is the best way to display the labels of those id's without making a complex sql query when displaying (have minimum of 6'ids) ?
The other options which is not very nice to do would be to call get_label(id,table,lang)
but of course you can see the problem for each column (Total queries = column * rows)
Any better solution or i'm stuck without doing the join on 6 tables ?
If it's helps i'm using kohana
here is what i have ...
and the subject table for the subject_id :
I have for every field_id a table that correspond .
In term of performance which is better making a join or just calling a query to get the specific label when needed . ?
You want to use a SQL JOIN for this.
SELECT t1.*, t2.subject_en, ...
FROM table1 t1
JOIN table2 t2 ON (t2.id = t1.subject_id)
A JOIN has a much better performane - you have only a single query which can be properly optimized by the database engine while doing a SELECT while iterating over the rows from the initial query would give you n+1 separate queries.
First of all, I have no control over the database structure, etc.
I need to use PHP to retrieve records by state and name but all the data is in multiple tables. Basically I need to try and understand how to get these two queries combined so that they do not run so slow.
First I get my record ID's in PHP. (Lets assume $query returns an array of IDs)
table_products = A bunch of products
$query = "SELECT id,name FROM table_products WHERE name = '".$name."';";
Than I need to iterate through these records (NOTE : There can be A LOT) and figure out where these IDs reside in another two tables that has the location information of where they could be at.
table_places = a table with a bunch of locations
link_table = Contains the relationships between product and location
$state = "somestate";
foreach($query as $row)
{
$query_two = "SELECT table_places.name, table_places.id, table_places.state, link_table.place_id, link_table.product_id
FROM table_places
INNER JOIN link_table
ON table_places.id = link_table.place_id
WHERE table_places.state = '".$state."' AND link_table.product_id = '".$row->id."';";
}
God I hope this made sense. I am no query guru so if I could get assistance in optimizing this to run faster, I would be grateful.
The pain is here:
foreach($query as $row) <<--- you are pinging the DB to death.
Combine the two queries:
SELECT
pl.name, pl.id, pl.state,
l.place_id, l.product_id,
pr.name
FROM table_places pl
INNER JOIN link_table l ON (pl.id = l.place_id)
INNER JOIN table_products pr ON (l.product_id = pr.id)
WHERE pr.name = '$name'
AND pl.state = '$state'
ORDER BY pr.name, pl.state
Make sure you put indexes on all fields used in the ON clauses and the where clauses.
You can join more than two tables in one query. Untested query would be something like;
SELECT * FROM table_products AS prod LEFT JOIN (link_table AS link, table_places AS places)
ON (prod.id=link.id AND prod.id=places.id)
WHERE some_field='some_value'
This will definitely get you some performance boost, as one query is most of times a lot faster than number-of-records query's (as you now loop through the records and query the db once per record)
The answer is simple: no control over database structure - no way to optimize.
As the indexing being the cornerstone of query optimization and you obviously need access to table structure to add one.