Normalise ID where 2 FK exist in row - mysql

I am trying to create a view to normalise "Sports Fixtures", however I have the ID of both the home team and the away team in a row in the fixtures table. When trying to normalise these, how can I get the team name of the relevant team?
select cast(`f`.`datetime` as date) AS `date`
, cast(`f`.`datetime` as time) AS `time`
, (select `t`.`name`
from (`fixturef_testing`.`teams` `t`
join `fixturef_testing`.`fixtures` `f`
on((`f`.`hometeamid` = `t`.`id`)))
where (`t`.`id` = `f`.`hometeamid`)) AS `hometeam`
, (select `t`.`name`
from (`fixturef_testing`.`teams` `t`
join `fixturef_testing`.`fixtures` `f`
on((`f`.`awayteamid` = `t`.`id`)))
where (`t`.`id` = `f`.`awayteamid`)) AS `awayteam`
, `u`.`name` AS `referee`,`c`.`name` AS `competition`
from ((`fixturef_testing`.`fixtures` `f`
left join `fixturef_testing`.`users` `u`
on((`u`.`id` = `f`.`refereeid`)))
left join `fixturef_testing`.`competition` `c`
on((`c`.`id` = `f`.`competitionid`)))
where (`f`.`active` = 1)
Fixtures has hometeamid and awayteamid
Teams has id and name
I have tried a a subquery but it returns multiple results.
Any help/advise is appreciated.
Teams
Fixtures

You need to join to teams twice, once for home team, once for away.
Think of it this way: for each foreign key you have to the same table, you need another join.
Though I'm not sure why you have the multiple joins to fixturef_testing one for user and one for completion...
Also, I'm not a fan of putting everything in the back ticks. I'd rather only use that when I have a reserved/keyword to make it more readable.
SELECT cast(f.`datetime` as date) AS `date`
, cast(f.`datetime` as time) AS `time`
, HT.Name hometeam
, AT.Name awayteam
, u.name AS referee
, c.name AS competition
FROM fixturef_testing.fixtures f
--Are the next 2 joins really needed?--
LEFT JOIN fixturef_testing.users u
on u.id = f.refereeid
LEFT JOIN fixturef_testing.competition c
on c.id = f.competitionid
--Not sure what the above joins are for...
--Get the table Teams record for the home team
LEFT JOIN Teams HT
on HT.ID = f.hometeamID
--Get the table Teams record for the away team
LEFT JOIN Teams AT
on AT.ID = f.awayTeamID
WHERE f.active = 1

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.

mysql query doesnot work

This is my mysql query!
SELECT projects.projects_id,
projects.projects_title,
projects.projects_cost
FROM projects
LEFT JOIN invoice
ON invoice.projects_id = projects.projects_id
LEFT JOIN project_assign
ON project_assign.projects_id=projects.projects_id
WHERE project_assign.assigned_user_id=3
AND (SUM( invoice.invoice_amount) < projects.projects_cost
OR invoice.projects_id is null )
AND project_assign.project_completed_date IS NOT NULL
In this query i want select all row that:
Is not present in other table e.g. (in my case other table is
"invoice")
Or if persent then this condition must hold sum(invoice.invoice_amount) < projects.projects_cost
Thanks.
Divide your problem. Use a UNION. First use one query to select all records that are not present in the first table (Use a LEFT JOIN), union that with the result of your second query which would give you all records meeting your second condition (use an outer join)
//Select all records present in left table
//union
//select all records present in both tables matching your condition.
SELECT projects.projects_id,
projects.projects_title,
projects.projects_cost
FROM projects
LEFT JOIN invoice
ON invoice.projects_id = projects.projects_id
LEFT JOIN project_assign
ON project_assign.projects_id=projects.projects_id
WHERE project_assign.assigned_user_id=3
AND project_assign.project_completed_date IS NOT NULL
UNION
SELECT projects.projects_id,
projects.projects_title,
projects.projects_cost
FROM projects
INNER JOIN invoice
ON invoice.projects_id = projects.projects_id
INNER JOIN project_assign
ON project_assign.projects_id=projects.projects_id
WHERE project_assign.assigned_user_id=3
AND (SUM( invoice.invoice_amount) < projects.projects_cost
AND project_assign.project_completed_date IS NOT NULL
select projects.projects_id, projects.projects_title,projects.projects_cost
from projects
left join invoice
on invoice.projects_id = projects.projects_id
left join project_assign
on project_assign.projects_id=projects.projects_id
where project_assign.assigned_user_id=3 and
((select sum(invoice.invoice_amount) from invoice) < projects.projects_cost or invoice.projects_id is null )
and project_assign.project_completed_date is not null
You cannot put aggregation functions in the where clause. In this case, you can do the aggregation using a subquery and then do the comparison:
SELECT p.projects_id, p.projects_title, p.projects_cost
FROM projects p LEFT JOIN
(select i.projects_id, sum(invoice_amount) as invoice_amount
from invoice i
) i
ON i.projects_id = p.projects_id LEFT JOIN
project_assign pa
ON pa.projects_id = p.projects_id
WHERE pa.assigned_user_id = 3 AND
(i.invoice_amount < p.projects_cost OR i.projects_id is null ) AND
pa.project_completed_date IS NOT NULL;

MySQL Joins where one value can be optional

Situation
I have a database which heavily makes use of joins due to the various situations in which each entity is used. Here is a simplified diagram:
Goal
I would like to be able to get details of all modules and the "name" fields regardless of whether the "fk_chapter_id" within user_has_module is set or not.
In the case where "user_has_module.fk_chapter_id" is null, the system can return details of the module and then null chapter.
In the case where there is a user_has_module, I would like to get the status
Issue
Whenever I perform SQL statements, I get the results only partially returned. I.E. If I have 4 module records in total, two of which where the user has an entry in "user_has_module" returns the two records in full and then 2 null records for the other modules.
Update based on feedback, almost there
Now, the only problem is I get duplicates. Using some test data
SELECT DISTINCT
chapter_id,
chapter_name,
module_id,
module_name,
(null ) AS user_module_progress,
(SELECT COUNT(fk_chapter_id) FROM module_has_chapter WHERE fk_module_id = m.module_id) AS chapter_count
FROM
module as m
LEFT JOIN
module_has_chapter as mhc ON m.module_id = mhc.fk_module_id
LEFT JOIN
chapter as c ON mhc.fk_chapter_id = c.chapter_id
group by m.module_id
UNION
SELECT DISTINCT
chapter_id,
chapter_name,
module_id,
module_name,
user_module_progress,
(SELECT COUNT(fk_chapter_id) FROM module_has_chapter WHERE fk_module_id = m.module_id) AS chapter_count
FROM
module as m
LEFT JOIN
user_has_module as uhm ON m.module_id = uhm.fk_module_id
LEFT JOIN
user as u ON uhm.fk_user_id = u.user_id
LEFT JOIN
chapter as c ON uhm.fk_latest_chapter_id = c.chapter_id
WHERE u.user_id = 2
group by m.module_id;
I got there in the end but, not particularly happy about it. This works but, it's a bloody mess...Does anyone have a better solution please?
SELECT DISTINCT
(null) AS chapter_id,
(null) AS chapter_name,
module_id,
module_name,
(null ) AS user_module_progress,
(SELECT COUNT(fk_chapter_id) FROM module_has_chapter WHERE fk_module_id = m.module_id) AS chapter_count
FROM
module as m
LEFT JOIN
user_has_module as uhm ON m.module_id = uhm.fk_module_id
WHERE
uhm.fk_user_id IS NULL
UNION ALL
SELECT DISTINCT
chapter_id,
chapter_name,
module_id,
module_name,
user_module_progress,
(SELECT COUNT(fk_chapter_id) FROM module_has_chapter WHERE fk_module_id = m.module_id) AS chapter_count
FROM
module as m
LEFT JOIN
user_has_module as uhm ON m.module_id = uhm.fk_module_id
INNER JOIN
user as u ON uhm.fk_user_id = u.user_id
INNER JOIN
chapter as c ON uhm.fk_latest_chapter_id = c.chapter_id
WHERE
u.user_id = 2;

How can I adjust a JOIN clause so that rows that have columns with NULL values are returned in the result?

How can I adjust this JOIN clause so that rows with a NULL value for the CountLocId or CountNatId columns are returned in the result?
In other words, if there is no match in the local_ads table, I still want the user's result from the nat_ads table to be returned -- and vice-versa.
SELECT u.franchise, CountLocId, TotalPrice, CountNatId, TotalNMoney, (
TotalPrice + TotalNMoney
)TotalRev
FROM users u
LEFT JOIN local_rev lr ON u.user_id = lr.user_id
LEFT JOIN (
SELECT lrr_id, COUNT( lad_id ) CountLocId, SUM( price ) TotalPrice
FROM local_ads
GROUP BY lrr_id
)la ON lr.lrr_id = la.lrr_id
LEFT JOIN nat_rev nr ON u.user_id = nr.user_id
INNER JOIN (
SELECT nrr_id, COUNT( nad_id ) CountNatId, SUM( tmoney ) TotalNMoney
FROM nat_ads
WHERE MONTH = 'April'
GROUP BY nrr_id
)na ON nr.nrr_id = na.nrr_id
WHERE lr.month = 'April'
AND franchise != 'Corporate'
ORDER BY franchise
Thanks in advance for your help!
try the following in where clause while making a left join. This will take all rows from right table with matched condition
eg.
LEFT JOIN local_rev lr ON (u.user_id = lr.user_id) or (u.user_id IS NULL)
Use this template, as it ensures that :
you have only one record per user_id (notice all subquerys have a GROUP BY user_id) so for one record on user table you have one (or none) record on subquery
independent joins (and calculated data) are not messed togeder
-
SELECT u.franchise, one.CountLocId, one.TotalPrice, two.CountNatId, two.TotalNMoney, (COALESCE(one.TotalPrice,0) + COALESCE(two.TotalNMoney,0)) TotalRev
FROM users u
LEFT JOIN (
SELECT x.user_id, sum(xORy.whatever) as TotalPrice, count(xORy.whatever) as CountLocId
FROM x -- where x is local_rev or local_ads I dont know
LEFT JOIN y on x.... = y.... -- where y is local_rev or local_ads I dont know
GROUP BY x.user_id
) as one on u.user_id = one.user_id
LEFT JOIN (
SELECT x.user_id, sum(xORy.whatever) as TotalNMoney, count(xORy.whatever) as CountNatId
FROM x -- where x is nat_rev or nat_ads I dont know
LEFT JOIN y on x.... = y.... -- where y is nat_rev or nat_ads I dont know
GROUP BY x.user_id
) as two on u.user_id = two.user_id

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