subquery: on clause is ambiguous - mysql

SELECT *
FROM (
`lecture` AS l
)
LEFT JOIN `professor` AS p ON `p`.`professor_id` = `l`.`professor_id`
WHERE `lecture_sem` = '20141'
AND (
lecture_name LIKE '%KEYWORD%'
OR lecture_code LIKE '%KEYWORD%'
OR p.professor_name LIKE '%KEYWORD%'
)
AND (
SELECT COUNT( DISTINCT s1.yoil, s1.start_time, s1.end_time )
FROM schedule AS s1
INNER JOIN schedule AS s2 ON ( s1.lecture_id
IN (
SELECT lecture_id
FROM timeitem
WHERE timetable_id =890
)
AND s2.yoil = s1.yoil
AND (
(
s1.start_time <= s2.start_time
AND s2.end_time <= s1.end_time
) )
AND s2.lecture_id = lecture_id # <-- HERE
)
) >0
LIMIT 0 , 30
I want to use where column like this:
s2.lecture_id = lecture_id
or,
s2.lecture_id = l.lecture_id
So I want to use parent column in subquery, but error occurs:
Column 'lecture_id' in on clause is ambiguous
I googled many answers about this problem ("on clause is ambiguous"), they said I should replace this query to joining two queries. But I don't have a clue how to transform this query.

I believe the following does the equivalent query, but I haven't tested it.
The technique is to move the correlated subquery into the FROM clause as a derived table so that it gets run only once, producing a result for each lecture_id (hence the GROUP BY).
I also factored out the subquery for timetable, which I believe can be rewritten as a JOIN.
And I suspect the join to professor may be properly an INNER JOIN -- how can you have a lecture without a professor?
SELECT l.*, p.*
FROM lecture AS l
INNER JOIN professor AS p ON p.professor_id = l.professor_id
INNER JOIN (
SELECT s2.lecture_id, COUNT( DISTINCT s1.yoil, s1.start_time, s1.end_time ) AS count
FROM schedule AS s1
INNER JOIN schedule AS s2 ON s2.yoil = s1.yoil
AND s1.start_time <= s2.start_time AND s2.end_time <= s1.end_time
INNER JOIN timeitem AS t ON s1.lecture_id = t.lecture_id
WHERE t.timetable_id = 890
GROUP BY s2.lecture_id
) AS c ON l.lecture_id = c.lecture_id
WHERE l.lecture_sem = '20141'
AND c.count > 0
AND (
l.lecture_name LIKE '%KEYWORD%'
OR l.lecture_code LIKE '%KEYWORD%'
OR p.professor_name LIKE '%KEYWORD%'
)
LIMIT 0 , 30
Anyway, even if the query isn't perfect, it demonstrates how one would refactor it to avoid a correlated subquery.

The line you identified in your code isn't an ON clause.
Instead, I think the error is referring to the following section.
AND (
SELECT COUNT( DISTINCT s1.yoil, s1.start_time, s1.end_time )
FROM schedule AS s1
INNER JOIN schedule AS s2 ON ( s1.lecture_id
IN (
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
SELECT lecture_id # <---- HERE
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FROM timeitem
WHERE timetable_id =890
)
You can fix this by creating an alias for the timeitem table, and prefixing the column with this prefix:
IN (
SELECT ti.lecture_id
FROM timeitem as ti
WHERE ti.timetable_id =890
)
But as Bill Karwin pointed out in his answer, you have other logical issues that need to be addressed.

Related

Getting The Latest Record Of Table By Date And Group By ID

Hi i have a problem of how to output the latest data of "reading_timesent" by using max(Date), And Also Include the other column where the latest date, and remove redundant data using distinct or group i don't know whats my problem here's the sample img and data
Patient table
Reading Table
Inner Join table
Expected Output
JSFIDDLE SAMPLE
SELECT * FROM Patients P
INNER JOIN Reading R ON R.patient_ID = P.patient_ID
Try this
SELECT * FROM
(
SELECT *,
(
SELECT reading_ID
FROM Reading
WHERE Reading.patient_ID = p.patient_ID
ORDER BY reading_timesent DESC
LIMIT 1
) AS newestReadingID
FROM Patients p
) AS subquery
JOIN Reading ON subquery.newestReadingID = Reading.reading_ID
You can use a correlated subquery for a condition in the ON clause:
SELECT *
FROM Patients P
INNER JOIN Reading R
ON R.patient_ID = P.patient_ID
AND R.reading_timesent = (
SELECT MAX(R2.reading_timesent)
FROM Reading R2
WHERE R2.patient_ID = P.patient_ID
)
Another way:
SELECT P.*, R.*
FROM (
SELECT patient_ID, MAX(reading_timesent) as reading_timesent
FROM Reading
GROUP BY patient_ID
) X
JOIN Patients P USING (patient_ID)
JOIN Reading R USING (patient_ID, reading_timesent)
Usually I would use the AUTO_INCREMENT column instead of a TIMESTAMP column for similar tasks. But that is not always applicable.

Counting one field in sql with join

I have a table, named rendeles_termekek.(In english, ordered_products)
I would like to count, that each product how many times was ordered.
With the SQL below, I get 4 as ennyi. I upload a picture, and I wrote the correct number to each rows.
SELECT DISTINCT
rendeles_termekek.termek_id,
termek.termek_id,
termek.termek_nev,
( SELECT COUNT(rendeles_termekek.termek_id) FROM rendeles_termekek ) AS ennyi
FROM rendeles_termekek
LEFT JOIN termek ON rendeles_termekek.termek_id = termek.termek_id
ORDER BY termek.termek_nev ASC
**r1** is the alias of your table rendeles_termekek so you have to access those columns through alias name "r1" like I did in below query. try below
SELECT DISTINCT
r1.termek_id,
termek.termek_id,
termek.termek_nev,
( SELECT COUNT(r.termek_id) FROM rendeles_termekek r where r.termek_id = r1.termek_id ) AS ennyi
FROM rendeles_termekek r1
LEFT JOIN termek ON r1.termek_id = termek.termek_id
ORDER BY termek.termek_nev ASC
I think you have only four rows in your table "rendeles_termekek".
I have updated your query. try this
SELECT DISTINCT
rendeles_termekek.termek_id,
termek.termek_id,
termek.termek_nev,
( SELECT COUNT(rendeles_termekek.termek_id) FROM rendeles_termekek r where r.termek_id = r1.termek_id ) AS ennyi
FROM rendeles_termekek r1
LEFT JOIN termek ON rendeles_termekek.termek_id = termek.termek_id
ORDER BY termek.termek_nev ASC
Note that I have changed the sub-query and added a where condition in it
`( SELECT COUNT(rendeles_termekek.termek_id) FROM rendeles_termekek r where r.termek_id = r1.termek_id ) AS ennyi`

Postgresql jsonb_agg subquery sort

How can I sort the results of a subquery that's using a json aggregate?
If I had a schema like this:
CREATE TABLE plans( id integer NOT NULL, name character varying(255));
CREATE TABLE plan_items ( id integer NOT NULL, plan_id integer NOT NULL, expected_at date, status integer);
I'm aggregating the plan_items result on a json column through a subquery.
Like this:
SELECT
plans.id,
plans.name,
jsonb_agg((SELECT pi_cols FROM
(SELECT plan_items.id, plan_items.expected_at, plan_items.status) pi_cols
)) AS plan_items_data
FROM
plans
INNER JOIN plan_items ON plan_items.plan_id = plans.id
GROUP BY
plans.id,
plans.name
ORDER BY plans.id;
The JSON aggregate is working as expected and give me the results that I need. Ok.
But I can't order the results.
I've tried:
jsonb_agg((SELECT pi_cols FROM
(SELECT plan_items.id, plan_items.expected_at, plan_items.status ORDER BY plan_items.expected_at) pi_cols
)) AS plan_items_data
and also:
jsonb_agg((SELECT pi_cols FROM
(SELECT plan_items.id, plan_items.expected_at, plan_items.status) pi_cols ORDER BY pi_cols.expected_at
)) AS plan_items_data
But none of these solved.
Any ideas?
As Abelisto suggests, just use a simple aggregate expression with ordering:
jsonb_agg(plan_items ORDER BY plan_items.expected_at) AS plan_items_data
Join the tables with the desirable sort order and use lateral join to select columns for jsonb_agg():
select s.plan_id id, name, jsonb_agg(pi_col)
from (
select p.id plan_id, p.name, pi.id, expected_at, status
from plans p
join plan_items pi
on p.id = pi.plan_id
order by p.id, expected_at
) s,
lateral (
select plan_id id, expected_at, status
) pi_col
group by 1, 2
order by 1;
The above query seems to be more natural and flexible (and a bit faster in most cases) than the one with a subquery in a select list. However for better performance you should also apply Abelisto's suggestion:
select s.plan_id id, name, json_agg(pi_col order by pi_col.expected_at)
from (
select p.id plan_id, p.name, pi.id, expected_at, status
from plans p
join plan_items pi
on p.id = pi.plan_id
) s,
lateral (
select plan_id id, expected_at, status
) pi_col
group by 1, 2
order by 1;

optimize sql select with many Joins

How can I optimize this mysql statement?
SELECT DISTINCT p.name
FROM Something_Meta s1
JOIN Something_Meta s2 ON s1.fk_somethingId = s2.fk_somethingId
JOIN Products p ON s2.fk_productId = p.id
JOIN
(
select fk_id from Restricted where fk_foo != 233 and fk_id NOT IN
(
Select fk_id from Restricted where fk_foo = 233
)
)
r ON r.fk_id = p.id
WHERE s1.fk_somethingId = 63 AND s2.fk_somethingId <> s1.fk_somethingId
order by p.name ASC
My tables are like that
Product (id,name )
Restricted (id,fk_id,fk_foo )
Something_Meta (id,fk_id,fk_somethingId )
fk_id is foreign key to product (id)
Probably that sql statement needs optimization..
select fk_id from Restricted where fk_foo != 233 and fk_id NOT IN
(
Select fk_id from Restricted where fk_foo = 233
)
The whole query statement needs more than 1.5 sec to run which are many secs for a website for a single query.
You could try an index on (fk_foo, fk_id) which would cover your entire query:
create index ix_restricted_fk_foo_fk_id(fk_foo, fk_id) on restricted
First off : the DISTINCT is a bit of a red flag. If you need to filter out the doubles then there's probably an error somewhere (either in the query or the design) causing the doubles.
Second; you could write your query like this:
SELECT DISTINCT p.name
FROM Something_Meta s1
JOIN Something_Meta s2 ON s1.fk_somethingId = s2.fk_somethingId
JOIN Products p ON s2.fk_productId = p.id
WHERE s1.fk_somethingId = 63 AND s2.fk_somethingId <> s1.fk_somethingId
AND NOT EXISTS ( SELECT *
FROM Restricted r
WHERE r.fk_foo != 233
AND r.fk_id = p.id )
AND EXISTS ( SELECT *
FROM Restricted r2
WHERE r2.fk_foo = 233
AND r2.fk_id = p.id )
order by p.name ASC
As I don't have a clue about the Something_Meta tables I'll simply focus on the Restricted table and suggest you put an index on fk_foo and fk_id. Said index is NOT part of the query, but rather of the table, so you have to define it upfront, once.
CREATE INDEX idx_Restricted ON Restricted (fk_id, fk_foo)
Once the index is there; any query that might benefit from it will automagically use it in the background; no need for you to adapt the query for it.
Side-note; since you obviously are looking for the product I find it curious you don't 'focus' your query on Products.
SELECT p.name
FROM Products p
JOIN etc...

MySQL: ORDER BY a COUNT of rows from another table?

SELECT e.*,
(
SELECT COUNT(*) FROM
(
SELECT tm.exerciseID FROM exercise_targetedmuscles tm
WHERE tm.exerciseID = e.exerciseID
AND tm.isPrimary = 1
AND tm.targetedMuscleID IN (4,11)
) as musclesCovered
) as numMusclesCovered
FROM exercises e
ORDER BY numMusclesCovered DESC
Basically, I want to order the exercises by the number of targetted muscles they cover (in the example, the targetted muscles are 4 and 11). However, the subquery (for some reason?) doesn't know what e is, so it does not work.
Any ideas as to how I can get this query to order my results properly? Thanks!
EDIT: came up with this after Randy's helpful comment.
SELECT COUNT(tm.targetedMuscleID) as numMusclesCovered, e.*, tm.* FROM exercise_targetedmuscles tm
JOIN exercises e ON tm.exerciseID = e.exerciseID
WHERE tm.isPrimary = 1
AND tm.targetedMuscleID IN (4,11)
GROUP BY tm.exerciseID
ORDER BY numMusclesCovered DESC
thanks so much!
Knowing how the tables are structured might be helpful. I suggest a solution similar to this.
SELECT e.*, tm.*, COUNT(tm.targetedMuscleID) AS coveredMuscles
FROM exercises AS e
JOIN exercise_targetedmuscles AS tm ON tm.exerciseID = e.exerciseID
WHERE tm.isPrimary = 1 AND tm.targetedMuscleID IN (4, 11)
GROUP BY e.exerciseID
ORDER BY coveredMuscles;
If I understand correctly, you don't need subquery at all
SELECT count(tm.targetedMuscleID) as num_muscles, e.exerciseID
FROM exercise_targetedmuscles tm, exercises e
WHERE tm.exerciseID = e.exerciseID
AND tm.isPrimary = 1
AND tm.targetedMuscleID IN (4,11)
GROUP BY e.exerciseID
order by num_muscles
SQL subqueries in MySQL has something like "local namespace". I haven`t got ability to test, but you may be searching for something like this:
SELECT `data`.* FROM (
SELECT COUNT(tm.targetedMuscleID) as `count`,tm.targetedMuscleID FROM exercise_targetedmuscles tm
LEFT JOIN `exercises` as `e` ON (`tm`.`exerciseID`=`e`.`exerciseID`)
WHERE
tm.isPrimary = 1
AND
tm.targetedMuscleID IN (4,11)
GROUP BY tm.targetedMuscleID
) as `data`
ORDER BY `data`.`count`
But, in some cases, you do not have to use subquery at all.