How do I consolidate these two subqueries? - mysql

I am writing an e-commerce query that polls for users with specific roles. The exact purpose of the query isn't important, all that matters is that it's working as intended right now. However, I really would like to clean up this section of the query
.
.
.
where u.firstname is not null
and u.users_id in (
select u.users_id from users u
inner join mbrrole mr on mr.member_id = u.users_id and mr.ORGENTITY_ID IN (
select ORGENTITY_ID from po_org_tree_v
)
inner join roledesc rd on rd.role_id = mr.role_id
where 1=1
and rd.displayname IN ('Customer Service Representative', 'Account Representative')
)
and u.users_id in (
select u.users_id from users u
inner join mbrrole mr on mr.member_id = u.users_id and mr.ORGENTITY_ID IN (
select ORGENTITY_ID from po_org_tree_v
)
inner join roledesc rd on rd.role_id = mr.role_id
where 1=1
and rd.displayname = 'Registered Customer'
);
As you can see, I have to nearly identical subqueries in the parent query's where clauses, where the only difference between then is the rd.displayname check. Is there some way in sql to comine two like queries into a "function", similar to actual functions in programming languages? How can I combine these subqueries to clean up the main query?

The code is checking that two different types of roles are available for each user.
Your current method might have the best performance, but you can simplify this down to one subquery:
where u.firstname is not null and
u.users_id in (select mr.member_id
from mbrrole mr inner join
roledesc rd
on rd.role_id = mr.role_id
where mr.ORGENTITY_ID IN (select ORGENTITY_ID from po_org_tree_v) and
rd.displayname IN ('Registered Customer', 'Customer Service Representative', 'Account Representative')
group by mr.member_id
having sum(rd.displayname = 'Registered Customer') > 0 and
sum(rd.displayname IN ('Customer Service Representative', 'Account Representative')) > 0
)
Note the following changes:
Removed users from the subquery. It is not needed.
Moved the ORGENTITY_ID condition to the where clause.
Added the GROUP BY and HAVING. The HAVING checks that both types of roles are defined for the user.

You can use this link (https://dev.mysql.com/doc/refman/5.7/en/create-procedure.html) to help you!
You'll need to use a procedure and not a function to return a result set so something like this should work (MySql is not my cup of tea):
CREATE PROCEDURE
procedureName(s String)
BEGIN
SELECT u.users_id
FROM users u
INNER JOIN mbrrole mr ON mr.member_id = u.users_id AND mr.ORGENTITY_ID
IN (
SELECT ORGENTITY_ID FROM po_org_tree_v
)
INNER JOIN roledesc rd ON rd.role_id = mr.role_id
WHERE rd.displayname = s;
END
And you call this procedure like this :
WHERE u.firstname IS NOT NULL
AND (u.users_id in procedureName('Customer Service Representative')
OR u.users_id in procedureName('Account Representative'))
AND u.users_id in procedureName('Registered Customer');

Related

Why this query is to slow in mysql

I try to improve this query but I do not made successefully. I use a some left join and subquerys (I don't know another form) We try to get all bookings from users with certain status and the number of bookings multiples related with this booking and get from the log user the origin. The query is slow even if use a limit clausure. I Appreciate all the help can you give me.
This is the query:
SELECT DISTINCT b.uneaque_id, b.id, b.status, b.route_status, b.username, b.purpose, b.transfer, b.pickup_date, b.pickup_time, b.return_time, b.amount, b.default_location, b.start_address_route_comments, b.start_address_route, b.end_address_route_comments, b.end_address_route, u1.first_name, u1.last_name, b.transaction_Id, b.manual_payment, mr.AddressCount, lu.origin
FROM bookingdetails b
LEFT JOIN log_users lu ON lu.uneaque_id = b.uneaque_id AND lu.command_type = 'NEW BOOKING'
LEFT JOIN (
SELECT booking_id, COUNT(*) AS AddressCount FROM booking_multiple_rides GROUP BY booking_id
) mr ON b.id = mr.booking_id,
userdetails u1 WHERE b.username = u1.email
AND u1.user_status IN ('Active', 'Blocked', 'Not_Active')
AND b.default_location = 1
PD: Sorry for my english.
You have a ON b.id = mr.booking_id, userdetails u1 WHERE
you should change with a proper inner join
SELECT DISTINCT b.uneaque_id
, b.id, b.status
, b.route_status
, b.username
, b.purpose
, b.transfer
, b.pickup_date
, b.pickup_time
, b.return_time
, b.amount
, b.default_location
, b.start_address_route_comments
, b.start_address_route
, b.end_address_route_comments
, b.end_address_route
, u1.first_name
, u1.last_name
, b.transaction_Id
, b.manual_payment
, mr.AddressCount
, lu.origin
FROM bookingdetails b
LEFT JOIN log_users lu ON lu.uneaque_id = b.uneaque_id AND lu.command_type = 'NEW BOOKING'
LEFT JOIN (
SELECT booking_id
, COUNT(*) AS AddressCount
FROM booking_multiple_rides GROUP BY booking_id
) mr ON b.id = mr.booking_id
INNER JOIN userdetails u1 ON b.username = u1.email
AND u1.user_status IN ('Active', 'Blocked', 'Not_Active')
AND b.default_location = 1
and be sure you have proper index on
table bookingdetails a composite index on columns ( uneaque_id , id, default_location)
table log_users a composite index on columns (uneaque_id, command_type)
table userdetails a cmposite index on columns (email,user_status )
Tip 1.
Hiding a derived table in a LEFT JOIN is a prescription for sluggishness.
Replace
mr.AddressCount
with
( SELECT COUNT(*)
FROM booking_multiple_rides
WHERE booking_id = b.id
GROUP BY booking_id ) AS AddressCount
and get rid of the LEFT JOIN ( SELECT ... ) AS mr ON ..
Tip 2 Use explicit JOINs, no the old-fashioned "comma-join":
JOiN userdetails u1
ON b.username = u1.email
This won't help performance but it will make things clearer.
Tip 3: If you need an INNER JOIN (u1) after a LEFT JOIN, use parentheses. Else, put the inner joins first, then the left joins. This makes it easier to use, but may screw up the logic.
Tip 4: Don't use LEFT unless you need it. When you dont need it, it confuses the reader (and the Optimizer). (Again, no performance change.)
Tip 5: Why are you using DISTINCT? That takes an extra pass over all the resultset.
If those do not help enough, then provide SHOW CREATE TABLE so we can critique the indexes.

Query inside NOT IN statement Doctrine

I found this sentence in the code:
$dql
= <<<DQL
SELECT u FROM AppBundle:User u
JOIN u.Roles r
JOIN u.team t
WHERE u.id NOT IN (
SELECT user.id
FROM GameBundle:Goal g
JOIN g.user user
WHERE
g.objective = :objective
)
AND
r.profile = :sales_profile
AND
r.company = :company
AND
u.onlyStatus NOT IN (:status)
DQL;
I don't know how to works that query inside NOT IN sentence, help me please.
I need to know:
What return the query inside of NOT IN, (data types, so on...)
How to works a query inside of NOT IN (is posible ?)
SELECT u FROM AppBundle:User u
JOIN u.Roles r
JOIN u.team t
WHERE u.id NOT IN (
SELECT user.id
FROM GameBundle:Goal g
JOIN g.user user
WHERE
g.objective = :objective
)
this means take all the users that don't currently have 'objective' in the 'Goal' table.
Is this what you needed ?

MySQL AS in JOIN statement

I have a table (edu_posts) which contains posts. The field "post_receiver" usually contains a user_id, but sometimes it may contain an event ID (syntax: event-ID) and I have created a statement like this:
SELECT
p.*,
u.firstname AS post_author_firstname,
u.lastname AS post_author_lastname,
u3.firstname AS receiver_firstname,
u3.lastname AS receiver_lastname,
pl.like_author AS user_likes,
CASE
WHEN p.post_receiver REGEXP '^[a-z]'
THEN
SUBSTRING_INDEX(
SUBSTRING_INDEX(p.post_receiver, '-', 2),
'-',
- 1
)
END
AS event_id,
e.event_name AS event_name
FROM
edu_posts p
LEFT JOIN edu_users u ON u.user_id = p.post_author
LEFT JOIN edu_users u3 ON u3.user_id = p.post_receiver
LEFT JOIN edu_likes pl ON pl.like_entity = p.post_id
LEFT JOIN edu_events e ON e.event_id = event_id
AND pl.like_author = 1
GROUP BY
p.post_id,
pl.like_id
ORDER BY
p.post_date DESC
Have a look at CASE and below in the SELECT statement, and the last LEFT JOIN.
I figured I cannot retrieve data from "event_id" i created in the SELECT statement. So what could I do to LEFT JOIN based on EVENT_ID's value?
Maybe this whole progress is too complicated, and instead it would be better and more effective to make a new MySQL call in the PHP code, if post_receiver is equal to: event-someID?
What are your thoughts? Thanks in advance!

Count number of occurrences based on user id

I'm very new to SQL/MySQL and Stackoverflow for that matter, and I'm trying to create a query through iReport (though I don't have to use iReport) for SugarCRM CE. What I need is to create a report that displays the number of "Referrals", "Voicemails", "Emails", and "Call_ins" that are linked to a specific "user" (employee). The query I currently have set up works; however it is running through the data multiple times generating a report that is 200+ pages. This is the code that I am currently using:
SELECT
( SELECT COUNT(*) FROM `leads` INNER JOIN `leads_cstm` ON `leads`.`id` = `leads_cstm`.`id_c` WHERE (leadtype_c = 'Referral' AND users.`id` = leads.`assigned_user_id`) ),
( SELECT COUNT(*) FROM `leads` INNER JOIN `leads_cstm` ON `leads`.`id` = `leads_cstm`.`id_c` WHERE (leadtype_c = 'VM' AND users.`id` = leads.`assigned_user_id`) ),
( SELECT COUNT(*) FROM `leads` INNER JOIN `leads_cstm` ON `leads`.`id` = `leads_cstm`.`id_c` WHERE (leadtype_c = 'Email' AND users.`id` = leads.`assigned_user_id`) ),
users.`first_name`,users.`last_name`
FROM
`users` users,
`leads` leads
I would appreciate any guidance!
You want to use conditional summation. The following uses MySQL syntax:
SELECT sum(leadtype_c = 'Referral') as Referrals,
sum(leadtype_c = 'VM') as VMs,
sum(leadtype_c = 'Email') as Emails,
users.`first_name`, users.`last_name`
FROM users join
`leads`
on users.`id` = leads.`assigned_user_id` INNER JOIN
`leads_cstm`
ON `leads`.`id` = `leads_cstm`.`id_c`
group by users.id;
You can use COUNT with CASE for this:
SELECT u.first_name,
u.last_name,
count(case when leadtype_c = 'Referral' then 1 end),
count(case when leadtype_c = 'VM' then 1 end),
count(case when leadtype_c = 'Email' then 1 end)
FROM users u
JOIN leads l ON u.id = l.assigned_user_id
JOIN leads_cstm lc ON l.id = lc.id_c
GROUP BY u.id
To match your exact results, you should probably use an OUTER JOIN instead, but this gives you the idea.
A Visual Explanation of SQL Joins

MySQL - How to indicate whether a user apply for a job or not?

I have a database schema like this picture:
I want to write a query that select data of all 6 tables and a field that indicate whether a specific user applied for a job or not.
I've tried:
SELECT j.id, j.expired_date, j.title, j.views, j.status
, j.unread, j.applicants, j.location, j.created_date
, j.contract_type, c.country
, group_concat(DISTINCT jp.page_name) AS fan_pages
, group_concat(DISTINCT jp.id_page) AS id_pages
, app.id AS applied
FROM jobs AS j
INNER JOIN country AS c ON c.id = j.country
LEFT JOIN job_pages AS jp ON jp.id_job = j.id
LEFT JOIN applications AS app ON app.id_job = j.id
LEFT JOIN resumes AS res ON res.id = app.id_resume
LEFT JOIN applicants AS alc ON alc.id = res.id_applicant
AND alc.id_user = 15
WHERE ( j.status = 0 )
AND ( j.expired_date = 0
OR j.expired_date > 1323228856 )
GROUP BY `j`.`id`
ORDER BY `j`.`id` desc
LIMIT 5 OFFSET 5
But it return a result that indicates a job was applied by any user. How can I rewrite this query?
-- Edit --
Below is a basic ERD for how it would be easier to track users who have applied to jobs. I made the relationship between User and Resume a 1:M, in case you wanted to track resume versions. If not, it should be a 1:1 relationship.
So given the ERD, you have a user apply to a job with a resume. If you want to make the resume optional, then you remove the Resume table from the M:M with Job and link directly to User.
Just some ideas...
-- Original --
Just some advice.
Seems to me that you may need to re-visit the schema design. It seems like the applicants table should be a pivot table between the users and jobs tables. The users and jobs table have a M:M relationship in that many users can apply to many jobs and many jobs can be applied to by many users. The applicants table should act as a transactional table when a user applies to a job.
Also, shouldn't the resumes table be directly linked to the users table? How can an application own a resume?
User owns a resume.
User applies to a job with a resume (applicant).
Try it,
SELECT j.id_user as creator, alc.id_user as applier, j.id , j.expired_date, j.title, j.views, j.status
, j.unread, j.applicants, j.location, j.created_date
, j.contract_type, c.country
, group_concat(DISTINCT jp.page_name) AS fan_pages
, group_concat(DISTINCT jp.id_page) AS id_pages
, MAX(app.id) AS applied
FROM jobs AS j
INNER JOIN country AS c ON c.id = j.country
LEFT JOIN job_pages AS jp ON jp.id_job = j.id
LEFT JOIN applications AS app ON app.id_job = j.id
LEFT JOIN resumes AS res ON res.id = app.id_resume
LEFT JOIN applicants AS alc ON alc.id = res.id_applicant
WHERE
( alc.id_user = 15 or alc.id_user IS NULL) AND
( j.status = 0 )
AND ( j.expired_date = 0
OR j.expired_date > 1323228856 )
GROUP BY `j`.`id`
ORDER BY `j`.`id` desc
UPDATE
I believe that, now the query is better:
SELECT
j.id, j.expired_date, j.title, j.views, j.status
, j.unread, j.applicants, j.location, j.created_date
, j.contract_type, c.country
, group_concat(DISTINCT jp.page_name) AS fan_pages
, group_concat(DISTINCT jp.id_page) AS id_pages
, max(app.id) AS applied
FROM users AS u
LEFT JOIN jobs AS j ON 1
INNER JOIN country AS c ON c.id = j.country
LEFT JOIN job_pages AS jp ON jp.id_job = j.id
LEFT JOIN applicants AS alc ON alc.id_user = u.id
LEFT JOIN resumes AS res ON res.id_applicant = alc.id
LEFT JOIN applications AS app ON app.id_resume = res.id AND app.id_job = j.id
WHERE u.id = 16 AND
( j.status = 0 )
AND ( j.expired_date = 0 OR j.expired_date > 1323228856 )
GROUP BY j.id
ORDER BY j.id
New updates:
Use MAX function if you want to get app.id because when you group one or more rows the max function will return correctly the id you want, else only first row will be return and it could be wrong with NULL
Join with the tables users and jobs
And join with applications should be with id_resume and id_job