Retrieve records from multiple tables some distinct, some not - mysql

I have 4 tables in an existing mysql database of a directory type site.
Table mt_links contains basic info for each listing
Table mt_cl contains which listing above is in what category (I only want cat_id=1)
Table mt_cfvalues contains more details for each listing It Can have repeated values
Table mt_images contains image names for each listing.
I want all records from mt_links where the mt_cl cat_id=1, and for each of those records, I need all records in mt_cfvalues and cf_images matching the link_id.
I set up a select with Group_Concat and left joins, but ended up with repeating values in my results. I added Distinct, which cured the repeating values, but mt_cfvalues can have records with the same value, so now I'm missing a value I should have.
SELECT a.link_id,
a.link_name,
a.link_desc,
GROUP_CONCAT(DISTINCT b.value ORDER BY b.cf_ID) AS details,
GROUP_CONCAT(DISTINCT c.filename ORDER BY c.ordering) AS images
FROM mt_links a
LEFT JOIN mt_cfvalues b ON a.link_id = b.link_ID
LEFT JOIN mt_images c ON b.link_id = c.link_ID
LEFT JOIN mt_cl d ON a.link_id = d.link_ID WHERE d.cat_ID = '1'
GROUP BY a.link_id
I put together a SQLFiddle here: http://www.sqlfiddle.com/#!2/f39e9/1
Is there an easier way? How do I fix the repeating / no repeating issue?

Here is one way of accomplishing what you seek. Because the two subqueries return independent results, you can't combine the GROUP BY, which is why you were getting duplicates.
SELECT a.link_id,
a.link_name,
a.link_desc,
cvf.details,
imgs.images
FROM mt_links a
LEFT JOIN (
SELECT link_ID, GROUP_CONCAT(value ORDER BY cf_ID) AS details
FROM mt_cfvalues
GROUP BY link_ID
) cvf ON cvf.link_ID = a.link_id
LEFT JOIN (
SELECT link_ID, GROUP_CONCAT(filename ORDER BY ordering) AS images
FROM mt_images
GROUP BY link_ID
) imgs ON imgs.link_ID = a.link_id
INNER JOIN mt_cl d ON a.link_id = d.link_ID
WHERE d.cat_ID = '1'

Related

group by but still displaying all data?

i wonder how to using group by but still displaying full data? i just want to group it.
here i give an example of my table :
this is my query :
(SELECT dp.menu_paket,d.id_detail,t.no_meja,m.nama_menu,d.jumlah,t.status,t.nama_pegawai
FROM menu m
join detail_paket dp on dp.menu_paket=m.nama_menu
JOIN detail_transaksi d on m.id_menu = d.id_menu
join transaksi t on t.id_transaksi=d.id_transaksi where t.status='progress' and d.status_menu='progress' group by id_detail)
UNION
(SELECT dp.menu_paket,d.id_detail,t.no_meja,p.nama_paket,d.jumlah,t.status,t.nama_pegawai
FROM paket p
join detail_paket dp on dp.id_paket=p.id_paket
JOIN detail_transaksi d on d.id_paket=p.id_paket
join transaksi t on t.id_transaksi=d.id_transaksi where t.status='progress' and d.status_menu='progress' group by id_detail);
thanks..!
You can apply distinct to avoid same multiple records instead of group by. because group by is used when there is aggregate function is your query.
Distinct retrieves single row instead of multiple rows when two rows are totally same.
Try this
select distinct columnname from table name
union
select distinct columnname1 from table name
I think I see two issues.
1) GROUP BY is generally used when you want to group rows for an aggregate function like SUM. You may be looking for ORDER BY, which controls the order of the rows. You can specify multiple columns for ORDER BY to obtain a "grouping" effect. This is what you want if you just want the rows to be next to each other in the list.
2) UNION, at least in the databases I know of, removes duplicate rows. You want UNION ALL if you want to preserve all rows.
Edit:
In response to the poster's comment, you definitely want ORDER BY and maybe UNION ALL. It should be ORDER BY no_meja, id_transaksi. Try the following query and see if it gives you what you want:
SELECT * FROM
((SELECT dp.menu_paket,d.id_detail,t.no_meja,m.nama_menu,d.jumlah,t.status,t.nama_pegawai
FROM menu m
join detail_paket dp on dp.menu_paket=m.nama_menu
JOIN detail_transaksi d on m.id_menu = d.id_menu
join transaksi t on t.id_transaksi=d.id_transaksi
where t.status='progress' and d.status_menu='progress')
UNION ALL
(SELECT dp.menu_paket,d.id_detail,t.no_meja,p.nama_paket,d.jumlah,t.status,t.nama_pegawai
FROM paket p
join detail_paket dp on dp.id_paket=p.id_paket
JOIN detail_transaksi d on d.id_paket=p.id_paket
join transaksi t on t.id_transaksi=d.id_transaksi
where t.status='progress' and d.status_menu='progress')) x
ORDER BY x.no_meja, x.id_transaksi;

mysql left join not returning empty row

I'm trying to get the list of categories with number of child records present in there. If the categories doesn't have records it should return NULL or 0 but my query returning categories with child records looks like its skipping the one without child records. ... will really appreciate the help.
here's my code:
SELECT
t_gal.f_sub_category_id,
t_sub_cat.f_sub_cat_name,
t_gal.f_image_thumb, (
SELECT COUNT(*)
FROM t_gallery
WHERE f_sub_category_id = t_gal.f_sub_category_id)
AS f_image_total
FROM t_gallery t_gal
LEFT JOIN t_sub_category t_sub_cat ON t_sub_cat.r_id = t_gal.f_sub_category_id
GROUP BY t_sub_cat.r_id
ORDER BY t_gal.f_added_on DESC, t_gal.r_id DESC
Here's the two tables:
Problem appears to be your group by clause.
You are grouping by a field that is on the LEFT JOINed table, hence when it does the group by all the rows which do not have a matching row on that table would appear to be aggregated into a single row.
I think what you are trying to get is a list of gallery items, along with the category they are in (if found) and the count of other galleries in the same category. If so try the following (if not let me know!)
SELECT t_gal.f_sub_category_id, t_sub_cat.f_sub_cat_name, t_gal.f_image_thumb, Sub1.GalleryCount
FROM t_gallery t_gal
LEFT JOIN t_sub_category t_sub_cat
ON t_sub_cat.r_id = t_gal.f_sub_category_id
LEFT OUTER JOIN (SELECT f_sub_category_id, COUNT(*) AS GalleryCount FROM t_gallery GROUP BY f_sub_category_id) Sub1
ON Sub1.f_sub_category_id = t_gal.f_sub_category_id
ORDER BY t_gal.f_added_on DESC, t_gal.r_id DESC
It LOOKS like for every sub-category (of a previously selected category), you want to include ALL of that sub-category... And, of those sub-categories, you want a count of how many images for that category wheather or not there even IS an image in the gallery table.
What you may have been running into is the select statement for the FIELD used to count images... first, that could become a performance killer. Instead, you could just do a left-join directly to the gallery table and COUNT the distinct R_IDs from the gallery FOR the corresponding sub-category
SELECT
t_sub_cat.r_id,
t_sub_cat.f_sub_cat_name,
MIN( COALESCE( t_gal.f_image_thumb, '' )) as JustOneThumbImg,
COUNT( DISTINCT( t_gal.r_id )) SubCategoryImageCount
FROM
t_sub_category t_sub_cat
LEFT JOIN t_gallery t_gal
ON t_sub_cat.r_id = t_gal.f_sub_category_id
GROUP BY
t_sub_cat.r_id
ORDER BY
t_sub_cat.f_added_on DESC
Since you are not grabbing all gallery images (since some may not exist FOR a given sub-category), ordering by the t_gal.r_id doesn't make sense
Also, the reason I'm not pre-grabbing aggregates in a sub-query to join against... I don't want to get everything from every category / sub-category without knowing which sub-categories are associated with the category you actually want.
the problem with your query is that you are using t_gallery as your main table and not t_sub_category while using left join.
you could try this: sqlfiddle
select
t_gal.f_sub_category_id,
t_sub_cat.f_sub_cat_name,
(
SELECT COUNT(*)
FROM t_gallery
WHERE f_sub_category_id = t_gal.f_sub_category_id)
AS f_image_total
from t_sub_category as t_sub_cat
left join t_gallery t_gal on t_gal.f_sub_category_id = t_sub_cat.r_id
GROUP BY t_sub_cat.r_id
ORDER BY t_gal.r_id DESC;

Multiple Grouping in mysql queries. Group_concat? Group_by? Inner joins? Where am I going wrong?

I'm finding trouble finding a similar example to what I'm trying to achieve. I have 3 tables. From one table I want to get the linking ID number. From another table I want to find the same ID's and add up another column of numbers in that table where the ID number from the 1st table matches. Then on the 3rd table, which is text, I want to group all the text together where the ID matches the main ID number... and return all this in 1 go. My diagram should show what I mean:
So have 2 queries that will on their own return part the results, but Im struggling to build it into 1 single query.
SELECT ticket_charges.ticket_id
, sum(ticket_charges.charge_time) AS Seconds
FROM
ticket_charges
LEFT OUTER JOIN tickets
ON ticket_charges.ticket_id = tickets.id
GROUP BY
ticket_charges.ticket_id
, tickets.id
The 77 and 937 for ticket ID 3 have been added up correctly!!
SELECT tickets.id AS `Ticket Number`
, left(tickets_messages.message, 500) AS `Ticket Message`
FROM
tickets
INNER JOIN tickets_messages
ON tickets.id = tickets_messages.id
GROUP BY
tickets_messages.ticket_id
, tickets.id
The messages are joined together correctly.
I've tried some concatenation on messages, selects within selects, different methods to group by, a couple of sums etc.. but just can't seem to get a result where by the I'm getting the results back correctly with both queries as 1 single query. Either the joined numbers from "charge_time" are very wrong and don't match any resemblance to anything or I end up with hundreds of "message" and strange numbers on the "charge_time"
FYI.. If I try this, I get "Sub query returned more than 1 row" but it's what I thought I should be doing.
SELECT ticket_charges.ticket_id
, sum(ticket_charges.charge_time) AS Seconds
FROM
ticket_charges
LEFT OUTER JOIN tickets
ON ticket_charges.ticket_id = tickets.id
Where (SELECT left(tickets_messages.message, 500)
FROM
tickets
INNER JOIN tickets_messages
ON tickets.id = tickets_messages.id
GROUP BY
tickets.id)
GROUP BY
ticket_charges.ticket_id
, tickets.id
If you really need to do that with a single query, the solution is to do a subquery in one of the jointures.
SELECT t.id, t.person_id, SUM(tc.charge_time), mc.concat
FROM tickets t
INNER JOIN tickets_charges tc ON tc.ticket_id = t.id
INNER JOIN (
SELECT ticket_id, GROUP_CONCAT(message SEPARATOR ' ') as concat
FROM tickets_messages
GROUP BY ticket_id) AS mc
ON mc.ticket_id = t.id
GROUP BY t.id
Try this query -
SELECT
t.id,
t.person_id,
SUM(tc.charge_time) Seconds,
GROUP_CONCAT(LEFT(tm.message, 20)) Message
FROM
tickets t
LEFT JOIN ticket_charges ts
ON ts.ticket_id = t.id
LEFT JOIN tickets_messages tm
ON tm.ticket_id = t.id
GROUP BY
t.id;
Note, that I used 'LEFT(tm.message, 20)', because GROUP_CONCAT function has length limitation - group_concat_max_len.

Attempting to Join 3 tables in MySQL

I have three tables that are joined. I almost have the solution but there seems to be one small problem going on here. Here is statement:
SELECT items.item,
COUNT(ratings.item_id) AS total,
COUNT(comments.item_id) AS comments,
AVG(ratings.rating) AS rate
FROM `items`
LEFT JOIN ratings ON (ratings.item_id = items.items_id)
LEFT JOIN comments ON (comments.item_id = items.items_id)
WHERE items.cat_id = '{$cat_id}' AND items.spam < 5
GROUP BY items_id ORDER BY TRIM(LEADING 'The ' FROM items.item) ASC;");
I have a table called items, each item has an id called items_id (notice it's plural). I have a table of individual user comments for each item, and one for ratings for each item. (The last two have a corresponding column called 'item_id').
I simply want to count comments and ratings total (per item) separately. With the way my SQL statement is above, they are a total.
note, total is the total of ratings. It's a bad naming scheme I need to fix!
UPDATE: 'total' seems to count ok, but when I add a comment to 'comments' table, the COUNT function affects both 'comments' and 'total' and seems to equal the combined output.
Problem is you're counting results of all 3 tables joined. Try:
SELECT i.item,
r.ratetotal AS total,
c.commtotal AS comments,
r.rateav AS rate
FROM items AS i
LEFT JOIN
(SELECT item_id,
COUNT(item_id) AS ratetotal,
AVG(rating) AS rateav
FROM ratings GROUP BY item_id) AS r
ON r.item_id = i.items_id
LEFT JOIN
(SELECT item_id,
COUNT(item_id) AS commtotal
FROM comments GROUP BY item_id) AS c
ON c.item_id = i.items_id
WHERE i.cat_id = '{$cat_id}' AND i.spam < 5
ORDER BY TRIM(LEADING 'The ' FROM i.item) ASC;");
In this query, we make the subqueries do the counting properly, then send that value to the main query and filter the results.
I'm guessing this is a cardinality issue. Try COUNT(distinct comments.item_id)

SQL Join returns only one record

I'm writing a query whereby I'm trying to count the total number of records in report and assignment table, whiles at the same time retrieving information from the main table group. Group has a primary key id which is saved in the other tables as gid. This is the query:
SELECT `group`.`id` AS `gid`
, `group`.`name` AS `g_name`
, COUNT(`report`.`id`) AS `reports`
FROM `group`
LEFT OUTER JOIN `report` ON `report`.`gid` = `group`.`id`
LEFT OUTER JOIN `assignment` ON `assignment`.`gid` = `group`.`id`
WHERE `group`.`active` = 0
ORDER BY
`group`.`name`;
My problem is whenever I execute this only one record is returned even if theirs multiple groups.
Thanks in advance.
Well, your query is far from correct :) First of all, you should not have aggregated functions (in this case count) without a group by clause. Now, even if you have that clause the query will summarize information and you want both: the detail and a summary in the same query. I'd recommend 2 separate queries to retrieve this information, but if you want information mixed in only one query (the detail and also the "total number of records in report and assignment table") try the following query:
SELECT
`group`.id AS gid,
`group`.name AS g_name,
(SELECT COUNT(*) from report) as ReportTotalCount,
(SELECT COUNT(*) from assignment) as AssignmentTotalCount,
FROM `group`
WHERE `group`.`active` = 0
LEFT OUTER JOIN report ON report.gid = `group`.id
LEFT OUTER JOIN assignment ON assignment.gid = `group`.id
ORDER BY `group`.name;
I whish I could understand exactly what you're looking for but this might give you an idea on how to get the result you expect.
Can't see anything obvious in your query that would limit it to returning one record.
You are going to have to break it up to see where the problem is against your existing data.
So how many groups where acitive = 0, ahow many with a corresponding assignment record, etc.
maybe it will help:
SELECT
groupid,
groupname,
reports,
assignments,
FROM
(SELECT group.id, group.name, COUNT(*) AS reports from group
INNER JOIN report ON (report.gid = group.id)
WHERE group.active = 0
GROUP BY group.id ) AS ReportForGroup
CROSS JOIN
(SELECT group.id AS groupid, group.name AS groupname, COUNT(*) AS assignments from group
INNER JOIN assignmentON (assignment.gid = group.id)
WHERE group.active = 0
GROUP BY group.id ) AS AssignmentForGroup
ON (ReportForGroup.groupid = AssignmentForGroup.groupid)
ORDER BY groupname;
I'm can't check it so if LEFT JOIN returns to COUNT(*) 0 or 1. if it returns 0 just change the INNERs to LEFTs and use INNER JOIN between the two queries