I will try to explain things as much as I can.
I have following query to fetch records from different tables.
SELECT
p.p_name,
p.id,
cat.cat_name,
p.property_type,
p.p_type,
p.address,
c.client_name,
p.price,
GROUP_CONCAT(pr.price) AS c_price,
pd.land_area,
pd.land_area_rp,
p.tagline,
p.map_location,
r.id,
p.status,
co.country_name,
p.`show`,
u.name,
p.created_date,
p.updated_dt,
o.type_id,
p.furnished,
p.expiry_date
FROM
property p
LEFT OUTER JOIN region AS r
ON p.district_id = r.id
LEFT OUTER JOIN country AS co
ON p.country_id = co.country_id
LEFT OUTER JOIN property_category AS cat
ON p.cat_id = cat.id
LEFT OUTER JOIN property_area_details AS pd
ON p.id = pd.property_id
LEFT OUTER JOIN sc_clients AS c
ON p.client_id = c.client_id
LEFT OUTER JOIN admin AS u
ON p.adminid = u.id
LEFT OUTER JOIN sc_property_orientation_type AS o
ON p.orientation_type = o.type_id
LEFT OUTER JOIN property_amenities_details AS pad
ON p.id = pad.property_id
LEFT OUTER JOIN sc_commercial_property_price AS pr
ON p.id = pr.property_id
WHERE p.id > 0
AND (
p.created_date > DATE_SUB(NOW(), INTERVAL 1 YEAR)
OR p.updated_dt > DATE_SUB(NOW(), INTERVAL 1 YEAR)
)
AND p.p_type = 'sale'
everything works fine if I exclude GROUP_CONCAT(pr.price) AS c_price, from above query. But when I include this it just gives one result. My intention to use group concat above is to fetch comma separated price from table sc_commercial_property_price that matches the property id in this case p.id. If the records for property exist in sc_commercial_property_price then fetch them in comma separated form along with other records. If not it should return blank. What m I doing wrong here?
I will try to explain again if my problem is not clear. Thanks in advance
The GROUP_CONCAT is an aggregation function. When you include it, you are telling SQL that there is an aggregation. Without a GROUP BY, only one row is returns, as in:
select count(*)
from table
The query that you have is acceptable syntax in MySQL but not in any other database. The query does not automatically group by the columns with no functions. Instead, it returns an arbitrary value. You could imagine a function ANY, so you query is:
select any(p.p_name) as p_num, any(p.tagline) as tagline, . . .
To fix this, put all your current variables in a group by clause:
GROUP BY
p.p_name,
p.id,
cat.cat_name,
p.property_type,
p.p_type,
p.address,
c.client_name,
p.price,
pd.land_area,
pd.land_area_rp,
p.tagline,
p.map_location,
r.id,
p.status,
co.country_name,
p.`show`,
u.name,
p.created_date,
p.updated_dt,
o.type_id,
p.furnished,
p.expiry_date
Most people who write SQL think it is good form to include all the group by variables in the group by clause, even though MySQL does not necessarily require this.
Add GROUP BY clause enumerating whatever you intend to have separate rows for. What happens now is that it picks some value for each result column and group_concats every pr.price.
Related
I have 7 tables to work with inside a query:
tb_post, tb_spots, users, td_sports, tb_spot_types, tb_users_sports, tb_post_media
This is the query I am using:
SELECT po.id_post AS id_post,
po.description_post as description_post,
sp.id_spot as id_spot,
po.date_post as date_post,
u.id AS userid,
u.user_type As tipousuario,
u.username AS username,
spo.id_sport AS sportid,
spo.sport_icon as sporticon,
st.logo_spot_type as spottypelogo,
sp.city_spot AS city_spot,
sp.country_spot AS country_spot,
sp.latitud_spot as latitudspot,
sp.longitud_spot as longitudspot,
sp.short_name AS spotshortname,
sp.verified_spot AS spotverificado,
u.profile_image AS profile_image,
sp.verified_spot_by as spotverificadopor,
uv.id AS spotverificador,
uv.user_type AS spotverificadornivel,
pm.media_type AS mediatype,
pm.media_file AS mediafile,
GROUP_CONCAT(tus.user_sport_sport) sportsdelusuario,
GROUP_CONCAT(logosp.sport_icon) sportsdelusuariologos,
GROUP_CONCAT(pm.media_file) mediapost,
GROUP_CONCAT(pm.media_type) mediaposttype
FROM tb_posts po
LEFT JOIN tb_spots sp ON po.spot_post = sp.id_spot
LEFT JOIN users u ON po.uploaded_by_post = u.id
LEFT JOIN tb_sports spo ON sp.sport_spot = spo.id_sport
LEFT JOIN tb_spot_types st ON sp.type_spot = st.id_spot_type
LEFT JOIN users uv ON sp.verified_spot_by = uv.id
LEFT JOIN tb_users_sports tus ON tus.user_sport_user = u.id
LEFT JOIN tb_sports logosp ON logosp.id_sport = tus.user_sport_sport
LEFT JOIN tb_post_media pm ON pm.media_post = po.id_post
WHERE po.status = 1
GROUP BY po.id_post,uv.id
I am having problems with some of the GROUP_CONCAT groups:
GROUP_CONCAT(tus.user_sport_sport) sportsdelusuario is giving me the right items but repeated, all items twice
GROUP_CONCAT(logosp.sport_icon) sportsdelusuariologos is giving me the right items but repeated, all items twice
GROUP_CONCAT(pm.media_file) mediapost is giving me the right items but repeated four times
GROUP_CONCAT(pm.media_type) mediaposttype s giving me the right items but repeated four times
I can put here all tables structures if you need them.
Multiple one-to-many relations JOINed in a query have a multiplicative affect on aggregation results; the standard solution is subqueries:
You can change
GROUP_CONCAT(pm.media_type) mediaposttype
...
LEFT JOIN tb_post_media pm ON pm.media_post = po.id_post
to
pm.mediaposttype
...
LEFT JOIN (
SELECT media_post, GROUP_CONCAT(media_type) AS mediaposttype
FROM tb_post_media
GROUP BY media_post
) AS pm ON pm.media_post = po.id_post
If tb_post_media is very big, and the po.status = 1 condition in the outer query would significantly reduce the results of the subquery, it can be worth replicating the original join within the subquery to filter down it's results.
Similarly, the correlated version I mentioned in the comments can also be more performant if the outer query has relatively few results. (Calculating the GROUP_CONCAT() for each individually can cost less than calculating it for all once if you would only actually using very few of the results of the latter).
or just add DISTINCT to all the group_concat, e.g., GROUP_CONCAT(DISTINCT pm.media_type)
I have the rather lengthy SQL query that I have included below. As you can see it orders by AvgRating and NumReviews, both of which rely on data from the reviews table. Unfortunately I need to see the rows in my results even when there are no reviews, currently if there are no reviews to order by then that row just doesnt show up in the results. All help greatly appreciated.
SELECT travisor_tradesperson.name, travisor_tradesperson.id, travisor_catagory.catname,
travisor_company.cname, travisor_company.description, travisor_company.city, travisor_company.address, travisor_company.postcode, travisor_company.phone,
ROUND(AVG(travisor_review.rating)) as RoundAvgRating, AVG(travisor_review.rating) as AvgRating, COUNT(travisor_review.rating) as NumReviews
FROM `travisor_tradesperson`
INNER JOIN travisor_company
ON travisor_tradesperson.company = travisor_company.id
INNER JOIN travisor_catagory
ON travisor_tradesperson.catagory = travisor_catagory.id
INNER JOIN travisor_review
ON travisor_review.tradesperson = travisor_tradesperson.id
WHERE travisor_catagory.catname = '$catagory'
AND travisor_company.city = '$city'
GROUP BY travisor_tradesperson.name, travisor_catagory.catname, travisor_company.cname,
travisor_company.description
ORDER BY AvgRating DESC, NumReviews DESC
Left join travisor_review instead of Inner Join. Inner join will only find records that are present in both tables. If you have no reviews for that tradesperson record, it will drop from the results set.
Left join will return a NULL if it cannot match on the join predicate. In this case, the tradesperson will return but with a NULL. Convert the NULL to a 0 if needed and that should fix your AVG.
I'm trying to select Posts with the associate numbers of Comments and Likes.
This is my query
SELECT `waller_posts`.*,
COUNT(waller_comments.id) AS num_comments,
COUNT(waller_likes.id) AS num_likes
FROM `waller_posts`
LEFT JOIN `waller_comments` ON `waller_comments`.`post_id` = `waller_posts`.`id`
LEFT JOIN `waller_likes` ON `waller_likes`.`post_id` = `waller_posts`.`id`
WHERE `wall_id` = 1
AND `wall_type` = "User"
GROUP BY `waller_posts`.`id`
When I add the second left join in this case of the likes, the results of the num_comments and num_likes came wrong. How can I perform this kind of query?
The query builds up to give you every possible combination of comments and likes on a post.
Probably easiest to just use COUNT(DISTINCT...) :-
SELECT `waller_posts`.*,
COUNT(DISTINCT waller_comments.id) AS num_comments,
COUNT(DISTINCT waller_likes.id) AS num_likes
FROM `waller_posts`
LEFT JOIN `waller_comments` ON `waller_comments`.`post_id` = `waller_posts`.`id`
LEFT JOIN `waller_likes` ON `waller_likes`.`post_id` = `waller_posts`.`id`
WHERE `wall_id` = 1
AND `wall_type` = "User"
GROUP BY `waller_posts`.`id`
Note that your query is relying on a feature of MySQL but which would cause an error in most flavours of SQL. For most flavours of SQL you need to list ALL the non aggregate columns in the GROUP BY clause.
Use Distinct clause because it will display combination of like and comment table data
SELECT `waller_posts`.*,
COUNT(DISTINCT waller_comments.id) AS num_comments,
COUNT(DISTINCT waller_likes.id) AS num_likes
FROM `waller_posts`
LEFT JOIN `waller_comments` ON `waller_comments`.`post_id` = `waller_posts`.`id`
LEFT JOIN `waller_likes` ON `waller_likes`.`post_id` = `waller_posts`.`id`
WHERE `wall_id` = 1
AND `wall_type` = "User"
GROUP BY `waller_posts`.`id`
I have the following MySQL query:
SELECT t.date_time, COUNT(t.submission_id) AS click_count, s.title, s.first_name, s.family_name, s.email, ut.ukip_name, a.advertiser_name
FROM recards.tracking t
INNER JOIN submissions s ON t.submission_id = s.submission_id
INNER JOIN form_settings fs ON t.form_id = fs.form_id
INNER JOIN advertisers a ON t.advertiser_id = a.advertiser_id
INNER JOIN db2829.ukip_titles ut ON fs.ukip_title_id = ut.ukip_title_id
WHERE t.advertiser_id = 621
GROUP BY t.submission_id
ORDER BY t.date_time DESC
However, of course, as the ORDER BY is done after the GROUP BY, I only get the first record, where as I need the last. Investigating, I read about using MAX() on the column during the SELECT, but that does not work with datetime types.
Is there another work around? Maybe embedding SELECTs within SELECTs? What I have above is about as far as my MySQL knowledge goes, unfortunately!
Turns out I was just forgetting the AS after the MAX(), with MAX(t.date_time) AS top_time it works fine.
My full query working:
SELECT MAX(t.date_time) AS top_time, COUNT(t.submission_id) AS click_count, s.title, s.first_name, s.family_name, s.email, ut.ukip_name, a.advertiser_name
FROM recards.tracking t
INNER JOIN submissions s ON t.submission_id = s.submission_id
INNER JOIN form_settings fs ON t.form_id = fs.form_id
INNER JOIN advertisers a ON t.advertiser_id = a.advertiser_id
INNER JOIN db2829.ukip_titles ut ON fs.ukip_title_id = ut.ukip_title_id
WHERE t.advertiser_id = 621
GROUP BY t.submission_id
ORDER BY top_time DESC
You are just grouping by t.submission_id and nothing else, so this query will not work. The other attributes need to be added to the GROUP BY or need to be eliminated from the SELECT.
As far as ordering add MAX(t.date_time) or MIN(t.date_time) to your SELECT and then ORDER BY that result either ascending or descending.
Please I need to figure out what I am doing wrong. I created this inner join code for mysql. it works but it gives me repeated values like repeating a particular row twice or categoryid twice. each of the tables(users,paymentnotification,monthlyreturns) has the categoryid used to check and display the username(users.pname) from the user table, then check and display those that have made payment from the monthly returns and payment table using the categoryid.
$r="SELECT monthlyreturns.categoryid, monthlyreturns.month, monthlyreturns.quarter, monthlyreturns.year,paymentnotification.amount, users.pname, monthlyreturns.ototal, paymentnotification.payee, status
FROM paymentnotification
INNER JOIN (monthlyreturns INNER JOIN users ON monthlyreturns.categoryid=users.categoryid)
ON monthlyreturns.categoryid=paymentnotification.categoryid
ORDER BY monthlyreturns.categoryid DESC";
I think the query you want is more like this:
SELECT b.categoryid, b.month, b.quarter, b.year, a.amount, c.pname, b.ototal, a.payee, status
FROM paymentnotification a
INNER JOIN monthlyreturns b
ON a.categoryid = b.categoryid
INNER JOIN users c
ON b.categoryid = c.categoryid
ORDER BY b.categoryid DESC
The way you are doing the correlations doesn't seem clear and may cause problems. Try this one out and see what happens. If its still doing duplicates, perhaps the nature of the data require further filtering.
Assuming I understand what you're trying to do, you are not joining your tables properly. Try joining one at a time
SELECT DISTINCT monthlyreturns.categoryid, monthlyreturns.month, monthlyreturns.quarter, monthlyreturns.year,paym entnotification.amount, users.pname, monthlyreturns.ototal, paymentnotification.payee, status
FROM paymentnotification
INNER JOIN monthlyreturns
ON paymentnotification.categoryid = monthlyreturns.categoryid
INNER JOIN users
ON monthlyreturns.categoryid = users.categoryid
ORDER BY monthlyreturns.categoryid DESC
I don't see any problem.. I get 4 result rows: check this fiddle http://sqlfiddle.com/#!2/165a22/5
this is the query I used:
SELECT m.categoryid, m.month, m.quarter, m.year,p.amount, u.pname, m.ototal, p.payee, m.status
FROM paymentnotification p JOIN monthlyreturns m ON p.categoryid = m.categoryid
JOIN users u ON u.categoryid = m.categoryid
ORDER BY m.categoryid DESC
there are no duplicated rows, just "unique" rows if you consider every column you choose.
Hope it helps
SELECT M.categoryid, M.month, M.quarter, M.year, M.ototal,
P.amount, P.payee, P.status,
U.pname
FROM paymentnotification AS P
INNER JOIN monthlyreturns AS M ON P.categoryid = M.categoryid
INNER JOIN users AS U ON M.categoryid = U.categoryid
ORDER BY M.categoryid DESC