In MySQL, how to select empty records - php

I have the following MySQL Table Structure:
mysql> desc customers;
+---------------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+-------------+------+-----+---------+-------+
| hash | varchar(32) | NO | PRI | NULL | |
| date_joined | date | NO | | NULL | |
| agent_code | int(5) | NO | UNI | | |
+---------------------+-------------+------+-----+---------+-------+
mysql> desc persons;
+--------------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+-------------+------+-----+---------+-------+
| agent_code | int(5) | NO | UNI | | |
| team_id | int(2) | YES | | 0 | |
| hash | varchar(32) | NO | PRI | NULL | |
+--------------------+-------------+------+-----+---------+-------+
mysql> desc teams;
+--------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
| leader | varchar(32) | NO | UNI | NULL | |
+--------+-------------+------+-----+---------+----------------+
And I'd wish to generate a report of sales by Team.
The SQL Query that I'm using is the following:
SELECT COUNT(`customers`.`agent_code) AS `customer_count`, `teams`.`name`
FROM `customers`
JOIN `persons` ON `customers`.`agent_code` = `persons`.`agent_code`
JOIN `teams` ON `persons`.`team_id` = `teams`.`id`
GROUP BY `teams`.`name`
And it shows the following information:
+----------------+--------+
| customer_count | name |
+----------------+--------+
| 3 | Team 1 |
+----------------+--------+
However I'd like to see the "customer_count" of all the teams in the database, even if the customer_count is null (or zero) for a given team. I have 15 teams in my database, so I'd like to see something like:
+----------------+--------+
| customer_count | name |
+----------------+--------+
| 3 | Team 1 |
| 0 | Team 2 |
| 0 | Team 3 |
| 0 | Team 4 |
+----------------+--------+
I have tried to execute some variants of the following Query, but I always get an error saying that the syntax of OUTER JOIN is incorrect, even though I've read the documentation, and it is correct.
SELECT COUNT( `customers`.`agent_code` ) AS `customer_count` , `teams`.`name`
FROM `customers`
LEFT JOIN `persons` ON `customers`.`agent_code` = `persons`.`agent_code`
LEFT OUTER JOIN `teams` ON `persons`.`team_id` = `teams`.`id`
GROUP BY `teams`.`name`
How can I alter my current query in order to display such result?

You have mistake get main table is person - I suggest your main table is team
SELECT COUNT(`customers`.`id`) AS `customer_count` , `teams`.`name`
FROM `teams`
JOIN `persons` ON `persons`.`team_id` = `teams`.`id`
LEFT JOIN `customers` ON `customers`.`agent_code` = `persons`.`agent_code`
GROUP BY `teams`.`name`
Update: if you do have empty teams, than you need to set left join on persons
SELECT COUNT(`customers`.`id`) AS `customer_count` , `teams`.`name`
FROM `teams`
LEFT JOIN `persons` ON `persons`.`team_id` = `teams`.`id`
LEFT JOIN `customers` ON `customers`.`agent_code` = `persons`.`agent_code`
GROUP BY `teams`.`name`

Related

Selecting mysql data across 3 tables where belongs and does not belong?

I have the following tables:
Course
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| c_id | bigint(20) | NO | PRI | NULL | auto_increment |
| c_name | varchar(255) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
Articles
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| a_id | bigint(20) | NO | PRI | NULL | auto_increment |
| a_name | varchar(255) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
Course_Articles
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| ca_id | bigint(20) | NO | PRI | NULL | auto_increment |
| a_id | bigint(20) | NO | | NULL | |
| c_id | bigint(20) | NO | | NULL | |
| t_id | bigint(20) | NO | | NULL | |
| sort_order | int(11) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
Term (or semester)
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| t_id | bigint(20) | NO | PRI | NULL | auto_increment |
| t_name | varchar(255) | NO | | NULL | |
| c_id | bigint(20) | NO | | NULL | |
| sort_order | int(11) | NO | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
I need to present the data from these tables as follows:
Course Name: Modern Gas Extraction
Articles:
Intro
Overview of gas extraction techniques
Term 1: Conventional Techniques
Coal Mine Methane
Another conventional technique
Another article not in a term
Another one
Term 2: Unconventional Techniques
Underground Coal Gasification
Coal Bed Methane
Hydraulic Fracturing
Conclusions
I know this data would benefit from perhaps nesting/tree. But it is what I have to work with. As you can see Articles can belong to a Term or be free standing.
I am a bit stumped as to how to efficiently query the above to output as per the example.
<?php
// UPDATED with UNION as suggested
try {
$stmt = $dbh->prepare(
"SELECT ca . * , a.a_name, t.t_name
FROM Course_Article AS ca
LEFT JOIN Article AS a ON a.a_id = ca.ca_id
LEFT OUTER JOIN Term AS t ON t.t_id = ca.t_id
WHERE ca.c_id = '2'
UNION SELECT te.t_name, te.t_id, te.c_id
FROM Term AS te
WHERE te.t_id = '2'");
$last_term_id = -1;
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
if ($last_term_id != $row['t_id']) {
echo "<b>" . $row['t_name'] . "</b><br />";
$last_term_id = $row['t_id'];
}
echo $row['a_name'] . "<br />";
}
} catch(PDOException $e) {
echo 'Error : '. $e->getMessage();
exit();
}
?>
Thank you
So here is one idea how to do it with union: first part gets all the terms (also the empty ones) and articles under them, and second part gets all the additional articles that belong to the course but not to any term. Hopefully I didn't include too many logic/typo mistakes here.
select t.t_name, ca.*, a.a_name
from Term t
left outer join Course_Article ca ON ca.t_id = t.t_id
left outer join Article a ON a.a_id = ca.a_id
where t.c_id = '2'
UNION
select null as t_name, ca.*, a.a_name
from Course_Articles ca
left outer join Article a ON a.a_id = ca.a_id
where ca.c_id = '2' and ca.t_id is null
Additionally, if you would need to order the result a bit (dunno if the sorting order is generic between the course article and term), you can extend this a bit (add whatever columns you need to the final result into the main select):
select t_name, a_name, sort_column
FROM
(
select t.t_name, ca.*, a.a_name, t.sort_order as sort_column
from Term t
left outer join Course_Article ca ON ca.t_id = t.t_id
left outer join Article a ON a.a_id = ca.a_id
where t.c_id = '2'
UNION
select null as t_name, ca.*, a.a_name, ca.sort_order as sort_column
from Course_Articles ca
left outer join Article a ON a.a_id = ca.a_id
where ca.c_id = '2' and ca.t_id is null
) dt
order by sort_column ASC

MySQL Query - select all comments for a post

I have these tables:
tbl_posts:
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(10) | NO | PRI | NULL | auto_increment |
| date | varchar(10) | NO | | NULL | |
| content | text | NO | | NULL | |
| status | tinyint(1) | NO | | NULL | |
| person_id | int(10) | NO | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
tbl_comments:
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(10) | NO | PRI | NULL | auto_increment |
| comment | varchar(10) | NO | | NULL | |
| post_id | int(10) | NO | | NULL | |
| person_id | int(10) | NO | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
and tbl_person which has person_id and some other fields...
i want to select all posts posted by a certain person and all comments for each of those posts (just like facebook)
i tried this query but it just bring me one comment for each post
select * from tbl_posts right join tbl_comments on tbl_posts.id = tbl_comments.post_id join tbl_person on tbl_person.id = tbl_posts.Person_id and tbl_comments.Person_id = 3 group by tbl_comments.id
If your foreign keys are set up correctly, an inner join should be fine. And, you don't need the group by:
select *
from tbl_posts join
tbl_comments
on tbl_posts.id = tbl_comments.post_id join
tbl_person
on tbl_comments.Person_id = 3;
Actually, I suspect the problem is tbl_person.id = tbl_posts.Person_id. Your query was returning only people who commented on their own posts.
When joining tables, you should always make sure there is a relationship between the tables you wish to join. In your case, this should give you all posts posted by a certain person and all comments for each of those posts:
SELECT *
FROM tbl_posts
LEFT JOIN
tbl_comments ON tbl_posts.id = tbl_comments.post_id
LEFT JOIN tbl_person ON tbl_comments.Person_id = tbl_person.id;
So instead of doing
tbl_comments.Person_id = 3
You need to JOIN the tbl_comments and tbl_person tables like so:
tbl_comments.Person_id = tbl_person.id

Get profile posts from friends

I'm making a small CMS, where you can have friends, and post on their walls. I'm making a page where you can view all the posts your friends have posted (on their profiles) however, I cannot get it to properly function.
Table: friends
+---------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------+------+-----+---------+----------------+
| friend_id | int(10) | NO | PRI | NULL | auto_increment |
| user1_id | int(10) | NO | | NULL | |
| user2_id | int(10) | NO | | NULL | |
| friends_since | int(10) | NO | | NULL | |
+---------------+---------+------+-----+---------+----------------+
Table: profile_posts
+----------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------+------+-----+---------+----------------+
| post_id | int(12) | NO | PRI | NULL | auto_increment |
| posted_user_id | int(12) | YES | | NULL | |
| user_id | int(12) | YES | | NULL | |
| date_posted | int(11) | YES | | NULL | |
| total_likes | int(12) | YES | | NULL | |
| users_liked | text | YES | | NULL | |
| total_dislikes | int(12) | YES | | NULL | |
| users_dislikes | text | YES | | NULL | |
| message | text | YES | | NULL | |
+----------------+---------+------+-----+---------+----------------+
Basically, if I want to determine if a user has posted on their profile as a status, the posted_user_id and user_id columns in the table profile_posts have to match (posted_user_id is the user's id of the post being posted on, and user_id is the user_id is the id of the person who posted that message).
However, I've tried multiple times to get friend's statuses.
SELECT *
FROM `friends` AS `f`
LEFT JOIN `profile_posts` AS `pp`
ON `pp`.posted_user_id = `pp`.user_id
WHERE
`f`.user1_id != 2 OR
`f`.user2_id != 2
ORDER BY `pp`.`date_posted` DESC
LIMIT 0,15;
(Hurray for not working! :D)
SELECT `f`.*,`pp`.*,
IF(`f`.user1_id=2, `f`.user2_id, `f`.user1_id) AS `uid`
FROM `friends` AS `f`
LEFT JOIN `profile_posts` AS `pp`
ON `pp`.posted_user_id =`uid`
WHERE
`f`.user1_id = `uid` OR
`f`.user2_id = `uid`
ORDER BY `pp`.`date_posted` DESC
LIMIT 0,15;
I'm not sure how to do this honestly, It's starting to make me rage as I cannot figure how to do this. :/
Never mind, figured out how to do it. :D
EDIT:
SELECT
`f`.*,
`pp`.*,
`ai`.`name`,
`ai`.`lastname`
FROM `friends` AS `f`
LEFT JOIN `profile_posts` AS `pp`
ON
`pp`.`posted_user_id` = `pp`.`user_id` AND
`pp`.`user_id` = `f`.user1_id OR
`pp`.`user_id` = `f`.user2_id
LEFT JOIN `account_info` AS `ai`
ON `ai`.`u_id` = `pp`.`user_id`
WHERE
`pp`.`user_id` = `pp`.`posted_user_id` AND
`f`.user1_id = 1 OR
`f`.user2_id = 1 AND
`pp`.`user_id` = `pp`.`posted_user_id`
GROUP BY `pp`.post_id
ORDER BY `pp`.date_posted DESC
LIMIT 0,20;
Selects the current user's friends, then it grabs all profile posts of their friends and then joins the account information table to get the name, and other information.
I'm trying to see if I can make this smaller, if not better..

Mysql one to many relationship. Order by number of rows in second table

I have created a voting system in php and mysql. When a user votes on an id, a record is inserted in "votes" referencing the FK media_id. When I then display the entries I use this query to get the number of votes for each entry:
$sql = "SELECT COUNT(*) FROM insta_votes WHERE media_id ='".$mediaid."'";
if ($res = $db->query($sql)) {
return $res->fetchColumn();
}
return 0;
This works fine, but I want to be able to sort the results by the number of votes they have. Preferably using just one query. How can I achieve this?
The tables are structured like this:
votes table
+-----------+--------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| media_id | varchar(255) | NO | | NULL | |
| ip | varchar(20) | NO | | NULL | |
| c_time | timestamp | NO | | CURRENT_TIMESTAMP | |
| sessionid | varchar(30) | NO | | NULL | |
+-----------+--------------+------+-----+-------------------+----------------+
entries table
+---------------+--------------+------+-----+-------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+-------------------+-------+
| page_id | int(11) | NO | MUL | NULL | |
| media_id | varchar(255) | NO | PRI | NULL | |
| url | varchar(255) | NO | | NULL | |
| c_time | datetime | NO | | NULL | |
| likes | int(11) | YES | | NULL | |
| deleted | tinyint(1) | NO | | 0 | |
| inserted_time | timestamp | YES | | CURRENT_TIMESTAMP | |
| numReports | int(11) | NO | | 0 | |
+---------------+--------------+------+-----+-------------------+-------+
Thank you!
If I understand the tables correctly (and I may not), each entries row may reference multiple votes rows. In that case, the query you need will go something like this:
SELECT
entries.page_id,
COUNT(*) AS VoteCount
FROM entries
INNER JOIN votes ON entries.media_id = votes.media_id
GROUP BY entries.page_id
ORDER BY VoteCount
If you add additional entries columns to the SELECT list, be sure to add them to the GROUP BY list as well.
Addendum: #JuanPabloCalifano pointed out, correctly, that this query won't include entries with zero votes. Here's how to include them:
SELECT
entries.page_id,
COALESCE(COUNT(votes.id), 0) AS VoteCount
FROM entries
LEFT JOIN votes ON entries.media_id = votes.media_id
GROUP BY entries.page_id
ORDER BY VoteCount
SELECT COUNT(*) as CNT, `media_id` FROM `insta_votes` GROUP BY `media_id` order by 1;
SELECT COUNT(*), media_id FROM insta_votes
GROUP BY media_id
ORDER BY COUNT(*);"

MySQL select statement giving error

This is the MySQL select statement I have that's giving me the error - Unknown column 'Regattas.regatta_id' in 'on clause'
SELECT
Regattas.regatta_id,
Events.event_id,
Events.event_name
FROM Regattas, Events
LEFT JOIN Regatta_Events AS Regatta_Events_1 ON Regatta_Events_1.fk_event_id = Events.event_id
LEFT JOIN Regatta_Events AS Regatta_Events_2 ON Regatta_Events_2.fk_regatta_id = Regattas.regatta_id
WHERE Regattas.regatta_id = {$regattaId}
The layout of the tables are like the below:
Regattas Table:
+--------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+----------------+
| regatta_id | int(11) | NO | PRI | NULL | auto_increment |
| regatta_name | varchar(100) | NO | | NULL | |
| regatta_start_date | date | NO | | NULL | |
| regatta_end_date | date | NO | | NULL | |
| regatta_start_time | time | NO | | NULL | |
| regatta_venue_id | int(11) | NO | | 0 | |
+--------------------+--------------+------+-----+---------+----------------+
Events Table:
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| event_id | int(11) | NO | PRI | NULL | auto_increment |
| event_name | varchar(255) | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
And the Regatta_Events table like this - Conjunction table:
+-------------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+---------+------+-----+---------+----------------+
| regatta_events_id | int(11) | NO | PRI | NULL | auto_increment |
| fk_regatta_id | int(11) | NO | | 0 | |
| fk_event_id | int(11) | NO | | 0 | |
+-------------------+---------+------+-----+---------+----------------+
Please Help me fix this I have been on it for a while.
Assuming this is what you're trying to do, you can use UNION ALL for this. You don't need the OUTER JOIN with the first query -- I left it just for reference (not 100% positive what you're trying to achieve):
SELECT
Regattas.regatta_id,
NULL event_id,
NULL event_name
FROM Regattas
LEFT JOIN Regatta_Events ON Regatta_Events.fk_regatta_id = Regattas.regatta_id
WHERE Regattas.regatta_id = {$regattaId}
UNION ALL
SELECT
NULL regatta_id,
Events.event_id,
Events.event_name
FROM Events
LEFT JOIN Regatta_Events ON Regatta_Events.fk_event_id = Events.event_id
I'm not completely sure I understand your desired results. This will return all results from the Events table, and only those results from the Regattas table where the id matches in the input.
Perhaps instead you are looking for something like this:
SELECT
Regattas.regatta_id,
Events.event_id,
Events.event_name
FROM Regattas
LEFT JOIN Regatta_Events ON Regatta_Events.fk_regatta_id=Regattas.regatta_id
LEFT JOIN Events ON Regatta_Events.fk_event_id=Events.event_id
WHERE Regattas.regatta_id = {$regattaId}
I think this should be simple join to return you Regetta_id, event_ID and event_name combination.
SELECT
Regattas.regatta_id,
Events.event_id,
Events.event_name
FROM Regattas as rg
LEFT JOIN Regatta_Events as re ON re.fk_event_id = rg.regatta_id
LEFT JOIN Events AS ev ON re.fk_regatta_id = ev.event_id
WHERE rg.regatta_id = {$regattaId}

Categories