using join if there is a record - mysql

I've a query like this:
select tractions.id,tractions.expired,tractions.succ,tractions.cradit_end_date,
tractions.user_id,users.family,tractions.coupon_code,users.username,traction_details.title,
traction_details_sub.title as sub_title,tractions.coupon_parent,tractions.coupon_id,tractions.coupon_property_id
from tractions,traction_details,traction_details_sub,users
WHERE
tractions.app='mobile'
AND tractions.succ = 1
AND tractions.user_id=$user_id
AND tractions.id = traction_details.tr_id
AND tractions.id = traction_details_sub.tr_id
AND tractions.user_id = users.id
now,some records in tractions have not any tr_id in traction_details_sub table.
how to check if traction_details_sub table have tr_id then join these tables ?

Switch to ANSI joins (they have been nearly universally available for a long time) and use an outer join for tables that may not have records:
SELECT
t.id
, t.expired
, t.succ
, t.cradit_end_date
, t.user_id
, u.family
, t.coupon_code
, u.username
, d.title
, s.title as sub_title
, t.coupon_parent
, t.coupon_id
, t.coupon_property_id
FROM tractions t
LEFT OUTER JOIN traction_details d ON t.id = d.tr_id
LEFT OUTER JOIN traction_details_sub s ON t.id = s.tr_id
JOIN users u ON t.user_id = u.id
WHERE t.app='mobile' AND t.succ = 1 AND t.user_id=$user_id
Note how this syntax moves tables away from the FROM clause into separate joins, with LEFT OUTER JOIN designating tables with optional rows. Also note how the last three conditions of your WHERE clause became ON conditions of the corresponding joins. Finally, note the use of table aliases (i.e. t for tractions, d for traction_details, s for traction_details_sub, and u for users) to shorten the query.

First select all tr_id from traction_details and after repeat you sql query add condition IN tractions.id (all id available)

Use left join for traction_details_sub table. If there is no matching record, then you will get tds.title as null.
select t.id,t.expired,t.succ,t.cradit_end_date,
t.user_id,u.family,t.coupon_code,u.username,td.title,
tds.title as sub_title,t.coupon_parent,t.coupon_id,t.coupon_property_id
from
tractions t
inner join
traction_details td
on t.id = td.tr_id
left join
traction_details_sub
on t.id = tds.tr_id
inner join
users u
on t.user_id = u.id
WHERE
t.app='mobile'
AND t.succ = 1
AND t.user_id=$user_id

Related

SQL Merge Two SELECT statements together

I have the following SQL query which returns 2 results which are the same only different by a where clause.
I would like to have them combined and group them under the user names. At the moment the two results are displayed but the results are not fully combined and the users are shown twice because of the two select statements.
How can I combine the two to create a single result.
The SQL query is as follows
SELECT
COUNT (asings.user_id),
post_status.status_id as status_id,
users.name,
users.id as user_id
from asigns
LEFT JOIN
post_status on asigns.post_id = post_status.post_id
RIGHT JOIN
users on asigns.user_id = users.id
WHERE
post_status.status_id = 2
GROUP BY users.id
UNION
SELECT
COUNT(asigns.user_id),
post_status.status_id as status_id,
users.name,
users.id from asigns
LEFT JOIN
post_status on asigns.post_id = post_status.post_id
RIGHT JOIN
users on asigns.user_id = users.id
WHERE
post_status.status_id = 3
GROUP BY users.id
See in this photo the usernames are appearing twice, what I want is to join the two username, and move count as two different columns depending on the status_id
How can I solve this. Thanks
You should use a single query with conditional aggregation:
SELECT
SUM(ps.status_id = 2) AS cnt_2,
SUM(ps.status_id = 3) AS cnt_3,
u.name,
u.id as user_id
FROM users u
LEFT JOIN assigns a ON a.user_id = u.id
LEFT JOIN post_status ps ON a.post_id = ps.post_id
GROUP BY u.id;
Use SELECT distinct for the union:
SELECT distinct * FROM ([FIRST SELECT] UNION [SECOND SELECT])
Or GROUP BY username
SELECT * FROM ([FIRST SELECT] UNION [SECOND SELECT]) GROUP BY username
BUT I suggest to review your both selects, if it differs just in the where clause so use where clause with the operator OR and you handle it just by one SELECT
Although your are using a LEFT and a RIGHT join and implicitly an INNER join between users and asigns, the WHERE clause transforms all the joins to INNER joins, because you are picking only the matching rows from post_status.
So, you only need INNER joins and conditional aggregation:
SELECT COALESCE(SUM(ps.status_id = 2), 0) counter_2,
COALESCE(SUM(ps.status_id = 3), 0) counter_3,
u.name,
u.id AS user_id
FROM users u
INNER JOIN asigns a ON a.user_id = u.id
INNER JOIN post_status ps ON ps.post_id = a.post_id
WHERE ps.status_id IN (2, 3)
GROUP BY u.id;

Improve MySql query left outer joins with subquery

We are maintaining a history of Content. We want to get the updated entry of each content, with create Time and update Time should be of the first entry of the Content. The query contains multiple selects and where clauses with so many left joins. The dataset is very huge, thereby query is taking more than 60 seconds to execute. Kindly help in improving the same. Query:
select * from (select * from (
SELECT c.*, initCMS.initcreatetime, initCMS.initupdatetime, user.name as partnerName, r.name as rightsName, r1.name as copyRightsName, a.name as agelimitName, ct.type as contenttypename, cat.name as categoryname, lang.name as languagename FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
left join Age a on c.ageLimit = a.id
left outer join (
SELECT contentId, createTime as initcreatetime, updateTime as initupdatetime from ContentCMS cms where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId WHERE c.deleted='0' order by c.id DESC
) as temp group by contentId) as c where c.editedBy='0'
Any help would be highly appreciated. Thank you.
Just a partial eval and suggestion because your query seems non properly formed
This left join seems unuseful
FROM ContentCMS c
......
left join (
SELECT contentId
, createTime as initcreatetime
, updateTime as initupdatetime
from ContentCMS cms
where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId
same table
the order by (without limit) in a subquery in join is unuseful because join ordered values or unordered value produce the same result
the group by contentId is strange beacuse there aren't aggregation function and the sue of group by without aggregation function is deprecated is sql
and in the most recente version for mysql is not allowed (by deafult) if you need distinct value or just a rows for each contentId you should use distinct or retrive the value in a not casual manner (the use of group by without aggregation function retrive casual value for not aggregated column .
for a partial eval your query should be refactored as
SELECT c.*
, c.initcreatetime
, c.initupdatetime
, user.name as partnerName
, r.name as rightsName
, r1.name as copyRightsName
, a.name as agelimitName
, ct.type as contenttypename
, cat.name as categoryname
, lang.name as languagename
FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
WHERE c.deleted='0'
) as temp
for the rest you should expiclitally select the column you effectively need add proper aggregation function for the others
Also the nested subquery just for improperly reduce the rows don't help performance ... you should also re-eval you data modelling and design.

sql query is behaving strange

I have a query
select c.CommentId
,c.CommentText
, c.CommenterId
, c.CommentDate
, u.first_name
, u.last_name
, i.ImageName
, i.Format
from comment c
join users u
on c.CommenterId = u.user_id
join user_profile_image i
on u.user_id = i.UserId
where PostId = 76
order
by CommentDate desc
limit 10
This query returns empty results when i.ImageName field is empty in the table. I want to return the row if the ImageName field is emty. How should I do this?
JOIN defaults to INNER JOIN for MySQL - try changing
join user_profile_image i
to
LEFT join user_profile_image i
The accepted answer here has a good visual explanation: Difference in MySQL JOIN vs LEFT JOIN
To include the rows when the ImageName field is empty, use LEFT JOIN, like this:
SELECT c.CommentId,c.CommentText, c.CommenterId, c.CommentDate, u.first_name,
u.last_name,i.ImageName,i.Format
FROM comment c
INNER JOIN users u ON c.CommenterId=u.user_id
LEFT JOIN user_profile_image i ON u.user_id=i.UserId
WHERE PostId = 76
ORDER BY CommentDate DESC
LIMIT 10;
The issue isn't exactly that i.ImageName is empty. The issue is that there is no image associated with the user. The join doesn't find an image, and without a match, the user isn't returned.
The solution is to use left join. My inclination is to write the query entirely with left join:
select c.CommentId, c.CommentText, c.CommenterId, c.CommentDate,
u.first_name, u.last_name,
i.ImageName, i.Format
from comment c left join
users u
on c.CommenterId = u.user_id left join
user_profile_image i
on u.user_id = i.UserId
where PostId = 76
order by c.CommentDate desc
limit 10;
Note: This assumes that PostId is in the comment table, which seems reasonable given the table names.

How can I use OR in a left join in MySQL?

Maybe there's a better way to do this... I have a table of member friend requests. The columns are request_id, author_id, recipient_id, status(accepted or denied). I also have a members table whose id is linked to the author or recipient. I want to get a list of a member's friends by selecting from the members table and then joining the requests table. Since the member can be either the author or the recipient of any, some, or none of the requests, a simple LEFT JOIN member_requests AS r ON member_id = r.author_id wouldn't work. How can I write a query that will do this?
SELECT
m.member_id, m.display_name
r.author_id, r.recipient_id, r.status
FROM members AS m
LEFT JOIN member_requests AS r ON m.member_id = r.recipient_id
WHERE r.status = 1 --Accepted
ORDER BY m.display_name
You can use an OR in your left join, like so:
LEFT JOIN member_requests AS r
ON m.member_id = r.recipient_id
OR m.member_id = r.author_id
However, your where clause also needs to be altered:
SELECT
m.member_id, m.display_name
r.author_id, r.recipient_id, r.status
FROM members AS m
LEFT JOIN member_requests AS r
ON (m.member_id = r.recipient_id
OR m.member_id = r.author_id)
AND r.status = 1 //Accepted
ORDER BY m.display_name
When you left join table A to table B, and then specify a restriction in your where clause on table B, you convert your left join into an inner join. Typically, a left join to table B would yield some null values, since table A might have records that don't join to table B. But if you say 'where table B.value = x', you restrict your join to only rows in which table A joins to table B, and furthermore to rows in which 'B.value = x'. The join is then evaluated as an inner join, rather than a left outer.

SQl queries left join not giving accurate results

I have been trying to join two tables (USERS AND USERS_ROLES) based on their role id I put the left join on following query
users.id = users_roles.fk_user_id
but the output is not correct of users_roles.fk_role_id coulmun and shows NULL where it should display the id of users.id = users_roles.fk_user_id that is 4 (at most places) because on users.id = users_roles.fk_user_id the value of users_roles.fk_role_id = 4
Kindly let me know how can i fix that so my query should result the exact vlaues of ids where they match,
Thanks
SELECT users.id, users.v_first_name, users.v_last_name, user_facility.fk_facility_id,users.fk_tenant_id, marital_status.v_marital_status,
users.v_blood_type, NOW(),users_roles.fk_role_id
FROM users
LEFT JOIN (user_facility, marital_status, users_roles) ON
users.id = user_facility.fk_user_id AND users.fk_marital_status_id=marital_status.id AND users.id = users_roles.fk_user_id
Usage of AND operator when used with Left or Right join gives different result. You should be clear what you are trying to accomplish..See this
well it is what you get by first implicitly inner-joining 3 tables and then explicitly left-joining the result to a 4th table only if 3 conditions relevant to all of the 3 inner-joinded tables are matched (i.e. when 3rd condition is false, nothing is joined from either of the 2 remaining tables)
i strongly suggest not to combine implicit and explicit joins, i personally use explicit joins all the time:
if you need an outer join:
SELECT ...
FROM users
LEFT JOIN user_facility ON users.id = user_facility.fk_user_id
LEFT JOIN marital_status ON users.fk_marital_status_id=marital_status.id
LEFT JOIN users_roles ON users.id = users_roles.fk_user_id
if you need an inner join:
SELECT ...
FROM users
JOIN user_facility ON users.id = user_facility.fk_user_id
JOIN marital_status ON users.fk_marital_status_id=marital_status.id
JOIN users_roles ON users.id = users_roles.fk_user_id
or if you prefere implicit inner joins for some obscure reason:
SELECT ...
FROM users,
user_facility,
marital_status,
users_roles
WHERE users.id = user_facility.fk_user_id
AND users.fk_marital_status_id=marital_status.id
AND users.id = users_roles.fk_user_id
(implicit outer joins are getting deprecated in all RDBMS as far as i know)
When it shows NULL it means there isn't a correspondency (relation) between all tables in the JOIN clause.
If you want to show only the ones that have relations in all tables, use INNER JOIN instead.
SELECT u.id,
u.v_first_name,
u.v_last_name,
uf.fk_facility_id,
u.fk_tenant_id,
ms.v_marital_status,
u.v_blood_type,
NOW(),
ur.fk_role_id
FROM users u
INNER JOIN user_facility uf ON u.id = uf.fk_user_id
INNER JOIN marital_status ms ON u.fk_marital_status_id=ms.id
INNER JOIN users_roles ur ON u.id = ur.fk_user_id