MySQL Delete: two similar codes, only one works - mysql

I have two similar SQL queries to solve a problem, though only the second one works.
The problem is:
Considering only muscular disorders, write a query to delete the onsets, which ended up with recovery, of patients that contracted and cured at least two (muscular) diseases.
The database has this structure:
Onset (Patient,Pathology,OnsetDate,RecoveryDate)
Pathology (Name,BodyPart)
This is my first code:
DELETE ES.*
FROM Onset ES NATURAL JOIN
(
SELECT E.Patient, E.Pathology, E.OnsetDate
FROM Onset E INNER JOIN Pathology P ON E.Pathology=P.Name
WHERE P.BodyPart='Muscles'
AND E.RecoveryDate IS NOT NULL
AND 2<=(SELECT COUNT(DISTINCT E2.Pathology)
FROM Onset E2 INNER JOIN Pathology P2 ON E2.Pathology=P2.Name
WHERE P2.BodyPart='Muscles'
AND E2.Patient=E.Patient
AND E2.RecoveryDate IS NOT NULL
)
) AS D;
This is my second code:
DELETE E.* FROM Onset E
INNER JOIN Pathology PA ON E.Pathology = PA.Name
NATURAL JOIN(
SELECT E2.Patient
FROM Onset E2 INNER JOIN Pathology P ON E2.Pathology = P.Name
WHERE E2.RecoveryDate IS NOT NULL
AND P.BodyPart = 'Muscles'
GROUP BY E2.Patient
HAVING COUNT(DISTINCT E2.Pathology) >= 2
) AS D
WHERE PA.BodyPart = 'Muscles'
AND E.RecoveryDate IS NOT NULL;
The second code works fine, instead the first one returns me the common error:
Error Code: 1093. You can't specify target table 'ES' for update in
FROM clause
I know that it happens when you try to delete from a table that you use inside a subquery, and that you can bypass this using a derived table.
Though, both my codes use Onset inside a subquery, and both use a derived table. So, why the first one doesn't work, while the second does?
Thanks in advance!

In delete clause remove the from clause
DELETE ES
NATURAL JOIN
(
SELECT E.Patient, E.Pathology, E.OnsetDate
FROM Onset E INNER JOIN Pathology P ON E.Pathology=P.Name
WHERE P.BodyPart='Muscles'
AND E.RecoveryDate IS NOT NULL
AND 2<=(SELECT COUNT(DISTINCT E2.Pathology)
FROM Onset E2 INNER JOIN Pathology P2 ON E2.Pathology=P2.Name
WHERE P2.BodyPart='Muscles'
AND E2.Patient=E.Patient
AND E2.RecoveryDate IS NOT NULL
)
) AS D;
be sure you have a proper (where) condition;

Related

SQL Delete Erro #1064

I don't know why this my DELETE sql command got the error number #1064.
My delete sql:
delete from nit.grades g where
(select id_dep from nit.offers as oo,nit.subjects as s where s.id=oo.id_subject and g.id_offer=oo.id)
!=(select id_dep from nit.students as stu where g.id_student=stu.id);
but this sql Select same where clause is working.
select * from nit.grades g where
(select id_dep from nit.offers as oo,nit.subjects as s where s.id=oo.id_subject and g.id_offer=oo.id)
!=(select id_dep from nit.students as stu where g.id_student=stu.id);
thanks for any help.
the Error message:
The syntax with the alias of the table you use for the delete statement is wrong.
Even more in the subqueries you use the target table and this is not allowed in MySql.
Instead you should use joins.
From your code this is what I understood that you want:
delete g
from nit.grades g
inner join nit.offers oo on g.id_offer = oo.id
inner join nit.subjects s on s.id = oo.id_subject
inner join nit.students st on g.id_student = st.id
where st.id_dep <> s.id_dep
In the WHERE clause I'm not sure if the columns id_dep are qualified correctly because they are not qualified also in your code.
If this is not what you want to do then use your SELECT query which does work (as you say) as a join to the table, provided there is a primary key like id in nit.grades:
delete g
from nit.grades g
inner join (
<your select query here>
) t
on t.id = g.id

Return the minimum of single field grouping in wider query

Here is a query:
select
e.eid,
e.event_time as contact_created,
cs.name as initial_class,
cn.create_time as lead_date
from bm_sets.event378 e
inner join bm_config.classes cs on cs.id = e.class_id and cs.cid=378 # and cs.name = 'Candidate'
left join bm_sets.conversion378 cn on cn.eid = e.eid and cn.create_time > e.event_time
where e.eid = 283818
group by eid, contact_created, initial_class, lead_date
The results of this query look like this:
eid, contact_created, initial_class, lead_date
283818 2015-03-07 09:43:42 Hot
283818 2015-03-10 22:19:47 Candidate
283818 2015-03-10 22:22:11 Candidate
I need to adjust this query so that only the first record is returned, the one with the min contact_created date. But since I'm using an aggregate function with other fields, I'm grouping by initial_class too so min is the min based on the combined groupings.
Our server seems to struggle whenever I use a subquery. So I tried using another join as a filter, something like:
inner join bm_sets.event378 e1 on e1.eid = e.eid and e1.event_time < e.event_time
But I know before running it that this won't work since the eid (user id) 283818 will still be returned and thus all associated data.
How can I restrict the results to only those records that correspond to the minimum of event_time?
I am using the where condition 283818 (my own user id for debugging) only as a sanity check as I construct this query. The query, when ready, will not have this condition and the results will thus be for many users.
If you need only top 1 then you can call just
select top 1
or you need make a function that returns you minimum created_contact then you just compare it with current created_contact.
I hope that will help you
Thanks
OK I got it. I used a null left join like so (on the back of other SO posts on the topic of groupwise min/max):
added this to selector:
e1.event_time
added this to joins:
left join bm_sets.event378 e1 on e1.eid = e.eid and e1.event_time < e.event_time
added this to where clause:
and e1.event_time is null

selecting from multiple tables vs JOIN

I need to display all fixtures(who plays 'against' who) for a current user so I wrote SQL query
SELECT
fixture.*
FROM
sport_team_player AS team_player, sport_team AS team
INNER JOIN sport_fixture AS fixture
ON (`team_player`.`team_id` = fixture.`team1_id` OR `team_player`.`team_id` = fixture.`team2_id`)
WHERE
team_player.`team_id` = team.`team_id` AND team_player.`player_id` = '16'
And this doesn't work and tells me that team_player.team_id does not exist
but if I join the second table instead of selecting from multiple tables it works just fine.
PS. This is not the best way to write such query but it's generated by ORM module..
EDIT:
Result would be list of fixture data like
------------------------------
|fixture_id|team1_id|team2_id|
------------------------------
|1 | 2 | 3 |
------------------------------
Try this one. Should result to the same query as yours;
SELECT fixture.*
FROM sport_team_player AS team_player
JOIN sport_team AS team
ON team_player.`team_id` = team.`team_id` AND team_player.`player_id` = '16'
INNER JOIN sport_fixture AS fixture
ON (`team_player`.`team_id` = fixture.`team1_id`
OR `team_player`.`team_id` = fixture.`team2_id`)
You shouldn't mix up both notations when building up joins. The comma you are using to join team_player and team , and the subsequent calls to inner join, will most probably trigger unknown column error.
Precedence of the comma operator is less than of INNER JOIN, CROSS JOIN, LEFT JOIN. That's why when you mix comma with other join table operators [Unknown column 'col_name' in 'on clause'] error occur. Same query will work if you specify the cross join ( to get a Cartesian product of the first two tables) instead of commas, because then in the from clause the table operators will be evaluated from left to right:
SELECT
fixture.*
FROM
sport_team_player AS team_player
cross join sport_team AS team
INNER JOIN sport_fixture AS fixture
ON (team_player.team_id = fixture.team1_id OR team_player.team_id = fixture.team2_id)
WHERE
team_player.team_id = team.team_id AND team_player.player_id = '16'
E.g.:
SELECT f.*
FROM sport_team_player p
JOIN sport_team t
ON t.team_id = p.team_id
JOIN sport_fixture f
ON p.team_id IN(f.team1_id,f.team2_id)
WHERE p.player_id = 16;

Delete from subquery

I am using Hibernate in my application. Currently I am trying to execute the following query:
DELETE FROM ActiveTimes a WHERE
a.begin>=:from AND a.begin<=:to
AND a.end>=:from AND a.end<=:to
AND a in(
SELECT al FROM ActiveTimes al
JOIN al.source.stage st
JOIN st.level.dataSource ds
WHERE ds=:dataSource)
But I get an error: Column 'id' in field list is ambiguous.
This feels normal, because the created SQL query looks like this:
delete
from
active_times
where
begin>=?
and begin<=?
and end>=?
and end<=?
and (
id in (
select
id
from
active_times activeti1_
inner join
sources sourc2_
on activeti1_.source=sourc2_.id
inner join
stage stage3_
on sourc2_.id=stage3_.source
inner join
levels levels4_
on stage3_.level=levels4_.id
inner join
datasources datasource5_
on levels4_.data_source=datasource5_.id
where
id=?
)
)
If I change the query to:
DELETE FROM ActiveTimes a WHERE
a.begin>=:from AND a.begin<=:to
AND a.end>=:from AND a.end<=:to
AND a.id in(
SELECT al.id FROM ActiveTimes al
JOIN al.source.stage st
JOIN st.level.dataSource ds
WHERE ds.id=:dataSource)
I get another error: You can't specify target table 'active_times' for update in FROM clause.
I am not very experimented with JPQL(or HQL) so I do not understand why the query looks like that in the first example.
The new error occurs because apparently I cannot make a subquery on the delete table in MySQL.
Do you have any suggestions on how can I rewrite one of the above queries in order to make it work?
Just remove the sub-query. It's unnecessary. I'm not sure how to write SQL code in Hybernate, but I'm guessing it would be something like this:
DELETE a
FROM ActiveTimes a
JOIN a.source.stage st
JOIN st.level.dataSource ds
WHERE a.begin>=:from AND a.begin<=:to
AND a.end>=:from AND a.end<=:to
AND ds.id=:dataSource;

MySQL involving crazy multiple self-joins

As part of the process of replacing some old code that used an incredibly slow nested select, I've ended up with a query that looks like this:
SELECT r3.r_id AS r3_id, r2.r_id AS r2_id, r1.r_id AS r1_id
FROM
table_r r3
LEFT JOIN (
table_r r2
INNER JOIN (
table_r r1
INNER JOIN table_d d ON r1.r_id = d.r_id
) ON r2.r_id = r1.parent_id
) ON r3.r_id = r2.r_id
WHERE d.d_id = 3
So in the innermost join, I'm looking for the records in table_r (copy r1) which have a relationship with a subset of records from table_d.
In the next join out, I'm looking for records in a second copy of table_r (r2) whose main index (r_id) matches the parent index (parent_id) of the records from the previous join.
Then I'm trying to do a LEFT JOIN with a third copy of table_r (r3), simply matching r_id with the r_id of the previous join. The idea of this outermost join is to get ALL of the records from table_r, but to then do the equivalent of a NOT IN select by using a further condition (not yet in my query) to determine which records in r3 have NULLs for r2_id.
The problem is that the LEFT JOIN is not giving me the whole of table_r. It's giving me the same subset of records that I get without the final join - in other words, the same thing as an INNER JOIN. So whereas I'm expecting 1208 records, I get 508.
I know I must be doing something screwy here...
What happens, if you try this?
SELECT r3.r_id AS r3_id, r2.r_id AS r2_id, r1.r_id AS r1_id
FROM
table_r r3
LEFT JOIN (
table_r r2
INNER JOIN (
table_r r1
INNER JOIN table_d d ON r1.r_id = d.r_id AND d.d_id = 3
) ON r2.r_id = r1.parent_id
) ON r3.r_id = r2.r_id
What I did was moved the d.d_id = 3 from where clause to the INNER JOINs ON qualifiers.