Edit: Using MySQL.
In this example I have a Posts table, a Users table, a Tagged_Posts table, and a Tags table.
The Posts table has a foreign key for user_id that corresponded to the Users table and id column.
The Tagged_posts table has two foreign keys, one for the tag_id and one for the post_id
The Tags table just has an id and a slug.
I'm trying to query posts and have all data related to that post returned, i.e The Post data, the user's data whom the post, and what tags belong to that post.
A single post can have many tags.
This is the SQL query I'm using:
$sql = "SELECT posts.title, users.name, users.handle, users.email, tags.slug as tags
from posts
INNER JOIN users ON posts.user_id = users.id
INNER JOIN tagged_posts ON posts.id = tagged_posts.post_id
INNER JOIN tags ON tagged_posts.tag_id = tags.id";
Since this particular post has 3 tags associated with it, the query returns the same post 3 times with different values listed for tag. See below:
Array
(
[0] => Array
(
[title] => This is my first Post!
[name] => exampleuser
[handle] => example
[email] => example#gmail.com
[tags] => database-design
)
[1] => Array
(
[title] => This is my first Post!
[name] => exampleuser
[handle] => example
[email] => example#gmail.com
[tags] => sql
)
[2] => Array
(
[title] => This is my first Post!
[name] => exampleuser
[handle] => example
[email] => example#gmail.com
[tags] => php
)
)
Am I able to do a single query and get everything related to the post?
You want to concatenate the results together. For instance, in MySQL, you would do:
select p.title, u.name, u.handle, u.email, group_concat(t.slug) as tags
from posts p join
users u
on p.user_id = u.id join
tagged_posts tp
on p.id = tp.post_id join
tags t
on tp.tag_id = t.id
group by p.title, u.name, u.handle, u.email;
Related
I am trying to get a count of apps for a user, using LEFT JOIN. It needs to return users regardless of whether they have apps, but the query is returning an empty record with 0 apps when no users are present.
SELECT u.*, COUNT(a.id) AS apps_working
FROM users u
LEFT JOIN apps a ON (u.id = a.user_id)
WHERE u.company_id = :company_id
AND u.account_type = 3
EDIT: When there are no users present, the results look like this:
Array
(
[0] => Array
(
[id] =>
[company_id] =>
[user_login] =>
[user_password] =>
[first_name] =>
[last_name] =>
[user_hash] =>
[user_slug] =>
[date_joined] =>
[last_login] =>
[account_type] =>
[status] =>
[apps_working] => 0
)
)
This is a query that is ALWAYS going to return a value because count(*) will always return something. If you don't want to see that, add a case statement to trap 0 and return null.
Try this code:
SELECT u.*, COUNT(IFNULL(a.id,0)) AS apps_working
FROM users u
LEFT JOIN apps a ON (u.id = a.user_id)
WHERE u.company_id = :company_id
AND u.account_type = 3
This is a left join query. So if there is no user satisfies conditions of company_id and account_type, there is no thing for joining and there is no thing in result.
Making a database for inbound calls with data for each type of call. I've defined a database with a "records" table, a "sales" table, and an "accounts_receivable" table. The "sales" and "accounts_receivable" tables are essentially subsets of the "records" table. Meaning, there is a corresponding row in the "records" table for each row of both the "sales" and "accounts_receivable" tables (exclusively).
My "records" table has values:
id (BIGINT)
timestamp (TIMESTAMP)
rep_id (INT)
notes (VARCHAR)
My "sales" table has values:
local_record_id (BIGINT)
record_id (BIGINT)
amount (VARCHAR)
bottles_sold (VARCHAR)
record_type (VARCHAR) default 'sales'
My "accounts_receivable" table has values:
local_record_id (BIGINT)
record_id (BIGINT)
amount (VARCHAR)
bottles_sold (VARCHAR)
record_type (VARCHAR) default 'ar'
I'm trying to pull all of the records in the entire database with the applicable data for each record. To do this, I thought a LEFT JOIN starting with the "records" table would work just fine, but for some reason it's not. This is my query:
SELECT *
FROM $table_name_records
LEFT JOIN $table_name_sales ON ($table_name_records.id = $table_name_sales.record_id)
LEFT JOIN $table_name_ar ON ($table_name_records.id = $table_name_ar.record_id)
This is returning a set of results where the data from the subset tables with similar column names are getting blanked out.
Example of output (which corresponds to an row/entry in the "sales" table of my database):
Array (
[id] => 1
[timestamp] => 2014-12-17 13:11:07
[rep_id] => 37
[notes] => Some notes for you.
[local_record_id] =>
[record_id] =>
[amount] =>
[bottles_sold] =>
[record_type] =>
)
I've verified that there is data in each of the columns of each of the subset tables of my database, so I don't understand why the values are coming back as blank. If I query with only a single left join:
SELECT *
FROM $table_name_records
LEFT JOIN $table_name_sales ON ($table_name_records.id = $table_name_sales.record_id)
I get what I expect to see:
Array (
[id] => 1
[timestamp] => 2014-12-17 13:11:07
[rep_id] => 37
[notes] => Some notes for you.
[local_record_id] => 14
[record_id] => 1
[amount] => 45.45
[bottles_sold] => Multiple
[record_type] => sales
)
Works the same way as I expect if I left join only the "accounts-receivable" table:
SELECT *
FROM $table_name_records
LEFT JOIN $table_name_ar ON ($table_name_records.id = $table_name_ar.record_id)
Array (
[id] => 1
[timestamp] => 2014-12-17 13:12:16
[rep_id] => 37
[notes] => Some notes.
[local_record_id] => 6
[record_id] => 2
[amount] => 50.89
[bottles_sold] => Single
[record_type] => ar
)
I've reviewed the awesome graphical SQL join doc MySQL Joins, and the LEFT JOIN there looks like exactly what I want: every thing from Table A (records) with the data from Table B (sales) and then also from a second Table B (accounts_receivable) attached to the appropriate rows (per the ON statement).
What am I doing wrong that this isn't responding as I'm expecting it to?
My queries where I only want "sales" records are typically structured in the other direction. Thus:
SELECT *
FROM $table_name_sales
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_sales.record_id)
So, I tried structuring my query from that direction and then just joining them via:
SELECT *
FROM $table_name_sales
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_sales.record_id)
UNION
SELECT *
FROM $table_name_ar
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_ar.record_id)
This query returns exactly what I'd expect:
Array (
[id] => 1
[timestamp] => 2014-12-17 13:11:07
[rep_id] => 37
[notes] => Some notes for you.
[local_record_id] => 14
[record_id] => 1
[amount] => 45.45
[bottles_sold] => Multiple
[record_type] => sales
)
But it seems to me that structuring a query in this way might get expensive in the future when I have lots of records and more tables (corresponding to different types of inbound calls -- eg: information request, problem with order, etc). I don't have a ton of experience with MySQL though.
It looks like your sales and accounts_receivable tables have parallel structures, and that rows in each of those tables are related to your records table but not to one another.
#MichaelBerkowski pointed out the hazard of trying to interpret a resultset from SELECT *.
It looks to me like you need the following sort of query.
SELECT r.id AS record_id, r.timestamp, r.rep_id, r.notes AS record_notes,
d.record_type, d.amount, d.bottles_sold
FROM records AS r
LEFT JOIN (
SELECT record_id, record_type, amount, bottles_sold FROM sales
UNION ALL
SELECT record_id, record_type, amount, bottles_sold FROM accounts_receivable
) AS d ON d.record_id = r.id
ORDER BY r.rep_id, r.id, r.timestamp, d.record_type DESC
This will present each rep's activity in order, interleaving the 'sales' and 'ar' records.
If you separately join the sales and accounts_receivable tables you'll get an unwanted combinatorial explosion. Doing UNION ALL prevents that.
Notice that you could use a single physical table for your sales and ar data.
I wrote a complex SQL query (which) run correctly:
select user.id,user.name,profile.info,score.ammount
from user,profile,score
where email = 'example#email.com'
AND user.id = profile.user_id
AND user.id = score.user_id
AND profile.type = 'language'
AND score.type = 'amount'
this query will return :
[id] => 10002096
[name] => Erik
[info] => English
[ammount] => 510710
the problem is if user.id not matched with profile.user_id, the previous matched results like(user.id, user.name) not returned? ( I want to return it even if next match not found)
if not matched want to return something like:
[id] => 10002096
[name] => Erik
how to fix this?
sample of profile table:
id user_id type info
1 1 language english
2 1 admin top
3 2 likes football
4 3 likes -
5 3 language english
user with id = 2 not has language
try left joining the tables instead of getting the Cartesian Product and filtering.. that is the old ansi way of joining tables and is more difficult to deal with as you add more tables.
SELECT u.id, u.name, p.info, s.ammount
FROM user u
LEFT JOIN profile p ON p.user_id = u.id AND p.type = 'language'
LEFT JOIN score s ON s.user_id = u.id AND s.type = 'amount'
WHERE email = 'example#email.com'
basically the left join says join the info and dont filter if there are rows that wont match
I have a working SQL statement, but there is one issue within it I can't solve.
When I left join my table sites_photos there can be multiple matches on sp.sites_id = s.id, but I want the table to only return 1. Is this possible.
SELECT s.*, sp.photo
FROM sites s
LEFT JOIN sites_photos sp
ON sp.sites_id = s.id
My output: 2 times id 30, but with different photo paths, I only want 1 returned for that id, or both bundled in one array.
Array
(
[0] => Array
(
[id] => 30
[url] => www.test.nl
[name] => Aleve
[date] => 2014-08-16
[cms_active] => Y
[archive] => N
[photo] => 2014080812365920120214103601number_1.jpg
)
[1] => Array
(
[id] => 30
[url] => www.test.nl
[name] => Aleve
[date] => 2014-08-16
[cms_active] => Y
[archive] => N
[photo] => 20140811021102news.jpg
)
)
You can do so,by using GROUP_CONCAT which will concatenate all the photos per site by and produces comma separated list of photos then you can use SUBSTRING_INDEX over result of GROUP_CONCAT to pick one photo,you can also add the criteria of order by in GROUP_CONCAT as GROUP_CONCAT(sp.photo ORDER BY sp.id DESC)
SELECT s.*, SUBSTRING_INDEX(GROUP_CONCAT(sp.photo),',',1) photo
FROM sites s
LEFT JOIN sites_photos sp
ON sp.sites_id = s.id
GROUP BY s.id
I have a join that works exactly as expected, except any and all fields selected from the 'right' table are returned blank when they definitely are not.
SELECT score.recipient, score.amount, u.* FROM score
LEFT JOIN `users` AS u ON score.recipient = u.id AND u.team_id = ?
WHERE UNIX_TIMESTAMP(score.date) > ?
I don't actually need the entire users table, only users.email - but no fields work. The result set looks like this (sample):
[0] => stdClass Object ( [recipient] => 1 [amount] => 1 [id] => [fname] => [lname] => [nickname] => [email] => [phone] => [reg_key] => )
[1] => stdClass Object ( [recipient] => 103 [amount] => -1 [id] => [fname] => [lname] => [nickname] => [email] => [phone] => [reg_key] => )
All of the fields listed are in fact populated.
Any help would be appreciated! I'm at a loss.
Your join condition / where clause is broken if replacing the left join with an inner join returns an empty result set.
Try this (without bind variables and their conditions) and see if it returns any values:
SELECT score.recipient, score.amount, u.* FROM score
LEFT JOIN `users` AS u ON score.recipient = u.id
If that's the case, then look at the values for team_id / score.date you get - I bet you're using a combination of bind values that simply does not exist in your tables.