简体   繁体   中英

JOIN 2 Tables with different columns

I Have 2 Tables, One For New Pictures and One For New Users, i want to create like a wall that mixes the latest actions so it'll show new users & pictures ordered by date.

What i want is a single query and how to know inside the loop that the current entry is a photo or user.

TABLE: users
Columns: id,username,fullname,country,date

TABLE: photos
Columns: id,picurl,author,date

Desired Output:


Daniel from California Has just registred 5mins ago


New Picture By David ( click to view ) 15mins ago


And so on...

I'm begging you to not just give me the query syntax, i'm not pro and can't figure out how to deal with that inside the loop ( i only know how to fetch regular sql queries )

Thanks

You could use an union:

SELECT concat(username, " from ", country, " has just registered") txt, date FROM users
UNION
SELECT concat("New picture By ", username, " (click to view)") txt, date FROM photos INNER JOIN users ON author=users.id
ORDER BY date DESC
LIMIT 10

This assumes that author column in photos corresponds to the users table id . If author actually is a string containing the user name (which is a bad design), you'll have to do this instead:

SELECT concat(username, " from ", country, " has just registered") txt, date FROM users
UNION
SELECT concat("New picture By ", author, " (click to view)") txt, date FROM photos
ORDER BY date DESC
LIMIT 10

Make sure you have an index on date in both tables, or this will be very inefficient.

I've put together this little example for you to look at - you might find it helpful.

Full script can be found here : http://pastie.org/1279954

So it starts with 3 simple tables countries, users and user_photos.

Tables

Note: i've only included the minimum number of columns for this demo to work !

drop table if exists countries;
create table countries
(
country_id tinyint unsigned not null auto_increment primary key,
iso_code varchar(3) unique not null,
name varchar(255) unique not null
)
engine=innodb;

drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
country_id tinyint unsigned not null,
username varbinary(32) unique not null
-- all other detail omitted
)
engine=innodb;

drop table if exists user_photos;
create table user_photos
(
photo_id int unsigned not null auto_increment primary key,
user_id int unsigned not null,
-- all other detail omitted
key (user_id)
)
engine=innodb;

The important thing to note is that the primary keys of users and photos are unsigned integers and auto_increment (1,2,3..n) so I can find the latest 10 users and 10 photos by ordering by their primary keys (PK) descending and add a limit clause to restrict the number of rows returned.

-- change limit to increase rows returned
select * from users order by user_id desc limit 2; 
select * from user_photos order by photo_id desc limit 2;

Test Data

insert into countries (iso_code, name) values ('GB','Great Britain'),('US','United States'),('DE','Germany');

insert into users (username, country_id) values ('f00',1),('bar',2),('stack',1),('overflow',3);

insert into user_photos (user_id) values (1),(1),(2),(3),(1),(4),(2),(1),(4),(2),(1);

So now we need a convenient way (single call) of selecting the latest 10 users and photos. The two tables are completely different so a union isnt going to be the best approach so what we'll do instead is write a stored procedure that returns two resultsets and handle generating the wall (merge resultsets) in our php script.

Stored procedure

Just a wrapper around some SQL code - think of it like SQL's version of a function call

drop procedure if exists list_latest_users_and_photos;

delimiter #

create procedure list_latest_users_and_photos()
begin

-- last 10 users
select 
    'U' as type_id, -- integer might be better
     u.user_id,
     u.country_id,
     u.username,
     -- other user columns...
     c.name as country_name
from 
 users u 
inner join countries c on u.country_id = c.country_id
order by 
 u.user_id desc limit 10;

-- last 10 photos

select 
 'P' as type_id, 
 up.photo_id,
 up.user_id,
 -- other photo columns...
 u.username 
 -- other user columns...
from 
 user_photos up
inner join users u on up.user_id = u.user_id
order by
 up.photo_id desc limit 10;

end #

delimiter ;

Testing

To test our stored procedure all we need to do is call it and look at the results.

mysql> call list_latest_users_and_photos();

+---------+---------+------------+----------+---------------+
| type_id | user_id | country_id | username | country_name  |
+---------+---------+------------+----------+---------------+
| U       |       4 |          3 | overflow | Germany       |
| U       |       3 |          1 | stack    | Great Britain |
| U       |       2 |          2 | bar      | United States |
| U       |       1 |          1 | f00      | Great Britain |
+---------+---------+------------+----------+---------------+
4 rows in set (0.00 sec)

+---------+----------+---------+----------+
| type_id | photo_id | user_id | username |
+---------+----------+---------+----------+
| P       |       11 |       1 | f00      |
| P       |       10 |       2 | bar      |
| P       |        9 |       4 | overflow |
| P       |        8 |       1 | f00      |
| P       |        7 |       2 | bar      |
| P       |        6 |       4 | overflow |
| P       |        5 |       1 | f00      |
| P       |        4 |       3 | stack    |
| P       |        3 |       2 | bar      |
| P       |        2 |       1 | f00      |
+---------+----------+---------+----------+
10 rows in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

Now we know that works we can call it from php and generate the wall.

PHP Script

<?php

$conn = new Mysqli("localhost", "foo_dbo", "pass", "foo_db");

$result = $conn->query("call list_latest_users_and_photos()");

$users = array();
while($row = $result->fetch_assoc()) $users[] = $row;

$conn->next_result();
$result = $conn->use_result();

$photos = array();
while($row = $result->fetch_assoc()) $photos[] = $row;

$result->close();   
$conn->close();

$wall = array_merge($users, $photos);

echo "<pre>", print_r($wall), "</pre>";

?>

Hope you find some of this helpful :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM