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.
Related
I have a table topics. And I have two queries which select different data, one for the table votings and one from the table messages. topics has a field m_group which is a foreign key for messages. This field can either be NULL or a group for entries in messages (group is a field in this table). If this field is set to a group, it should perform query 1 to select all messages or if it is not set it should perform query 2 to select all votings for this specific topic. I'm using Postgres and PHP on an Apache Webserver.
Now my question is what is the recommended way to go. I came up with two solutions (not sure if solution 2 is actually possible, haven't tried it yet).
Solution 1
First select the field m_group. Then determine if it is set via PHP and perform the associated query.
Solution 2
Use a IF THEN ELSE statement
Basically the query should then look something like this
IF t.m_group IS NULL
THEN
query2
ELSE
query1
As already mentioned, I'm not sure if solution 2 is possible. What would be the best way to handle this? Solution 1 performs two queries, I think this is inefficient.
UPDATE
As mentioned above, it should perform the queries for a specific topic. You have the id of this topic. How can you specify this in solution 2? And does the IF THEN ELSE statement already know the alias t for topics, if that is specified in the queries?
If I understand you right, you need to use two subqueries as sets of data, while you should use one of them as a source depending on what's in the m_group field. Your second approach is good if you only select a few rows. However, if you need to grab a lot of data from the table, this way you will need to perform too many subqueries. I would rather first grab all the data you need from topics and then select what you need with both queries.
Pseudo SQL query will look like that:
with t1 as (select t.id, group_m, ... from topics t where t.m_group is not null),
t2 as (select t.id, ... from topics t where t.m_group is null)
select id, title, SUM(subtable_id) AS votes
from query1
join t1 on t1.id = query1.id and...
union
select id, title, SUM(subtable_id) AS votes
from query2
join t2 on t2.id = query2.id and...
We need to grab the last and newest 20 entries from different tables. However, the GROUP BY statement skips records because we are working with LEFT JOIN on tables.
All these records are linked to unique persons in another table. We store these person's id's in an array for more queries later.
We have a few tables (in which all those person id's are stored) and we want to get them sorted and grouped.
The tables are like this:
SELECT lastRecord+personID FROM t1
SELECT lastRecord+personID FROM t2
SELECT lastRecord+personID FROM t3
SELECT lastRecord+personID FROM t4
WHERE t5.Essential_Column_Name = '1'
GROUP BY personID
ORDER BY 'all the latest entries'
LIMIT 20
With that, the relevance of all the latest entries should be equal.
We do have a timestamp column as well. Perhaps that might work better.
Any input is highly appreciated!
For people looking for an answer on this; this is the right post, answer and update to this Q:
UNION mysql gives weird numbered results
With thanks to all for the ideas and providing the paths to the right solution.
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.
I have one query that INNER JOINs Table A with Table B and Table C, and I have another query that INNER JOINs Table A with Table D. I could achieve what I want to do by merging the 2 results, removing duplicates and ordering them in PHP code, but I want to know if MySQL has this functionality, I also think it'd be faster and easier to code once I understand it. Essentially, I want to have the results from Query 1 OR from Query 2. Perhaps the following will help:
Query 1:
SELECT pkAssociation, associations.strNameEn, associations.strNameFr
FROM associations
INNER JOIN eventassociations ON fkAssociation = pkAssociation
INNER JOIN events on fkEvent = pkEvent
WHERE events.enumDeleted = 'no'
GROUP BY pkAssociation
Query 2:
SELECT pkAssociation, associations.strNameEn, associations.strNameFr
FROM associations
INNER JOIN associationprograms AS aprogs ON aprogs.fkAssociation = associations.pkAssociation
GROUP by pkAssociation
The tables don't have anything else of relevance that don't show up in the query. I'm sorry if I'm not asking this correctly, I don't even know how to ask a question about this properly. If the column names or sample data is needed, then I can provide some. Sorry for the inconvenience and long post.
You want the UNION DISTINCT statement, placed between the two queries. This will combine the result sets from both and remove duplicates. You can then place your ORDER BY clause after all UNIONS (if you have one).
For example:
SELECT col1, col2 FROM tableA
UNION DISTINCT
SELECT col1, col2 FROM tableB
ORDER BY col2 DESC
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.