简体   繁体   中英

MYSQL converting slow update with subquery to (faster?) join

I have a query that uses a subquery in mysql that gets very slow, taking up to a few minutes while all other querys i do are quite fast.

As i found out subquerys are most likely a bad idea so i wanted to convert this subquery to a join like i did with all my other querys that used subquerys with big impacts on performance.

Most of my other querys where quite simple but this one drives me nuts.

Here is an example.

I have clients and bills. Clients have multiple bills. Bills have states. And bills have a parameter "whatever".

I need to update "all clients where all bills are in state 1 or 2 and that bills whatever-parameter is not 2"

Since i couldnt find out how to do that i used the invert which is "all clients that have no bills that are not in state 1 and not in state 2 and that bills whatever-parameter is not 2"

These are the two variants with subquerys i am using right now,one using count and one using "not in", but they seem to be equally slow.

update client cl , bill bi 
set cl.parameter = "parameter1" 
where cl.parameter="parameter2" 
    and bi.whatever != "2" 
    and bi.client_id = cl.id  
    and (select count(cl.id) 
            from bill bi 
                where bi.client_id = cl.id 
                    and bi.state!="state0" 
                    and bi.state != "state1" 
        ) = 0;

gets slow in mysql state "sending data"

update client cl , bill bi 
set cl.parameter = "parameter1"  
    where  cl.parameter="parameter2" 
        and bi.whatever != "2" 
        and bi.client_id = cl.id  
        and cl.id not in  (select distinct cl.id 
                                from bill bi  
                                    where bi.client_id = cl.id 
                                        and  ( bi.state!="state1" 
                                        and bi.state != "state2" 
                            ) ;

gets slow in mysql state "copying to temp table"

I tried for hours but i couldn't convert this to something usefull without that slow subquery. Can anyone give me an idea how to do this using a join or something faster than now?

UPDATE

Thanks to DRapp, this produces the exact same results and is much faster. For what i could test till now query time is down to a few seconds, and it was a few minutes before.

select
  c.id,
  sum( if( b.State IN ( "State1", "State2" ), 1, 0 )) as OkStatesCnt,
  sum( if( b.State NOT IN ( "State1", "State2" ) or b.whatever=2, 1, 0 )  ) as AnyOtherState
from
  client c
     join bill b
        ON c.id = b.client_id
where
  c.parameter = "parameter2"
   group by
      c.id
  having
      OkStatesCnt > 0
  AND AnyOtherState = 0

And

UPDATE client cl,
  ( full select query from above ) as PreQualified
 set cl.parameter = "parameter1"
 where cl.id = PreQualified.id

To pre-ensure WHAT clients will be included, you can run this query on its own...

select
      c.id,
      sum( if( b.State IN ( "State1", "State2" ), 1, 0 )) as OkStatesCnt,
      sum( if( b.State IN ( "State1", "State2" ), 0, 1 )) as AnyOtherState
   from
      client c
         join bill b
            ON c.id = b.client_id
           AND b.whatever != "2"
   where
      c.parameter = "parameter2"
       group by
          c.id
   having
          OkStatesCnt > 0
      AND AnyOtherState = 0

IF this in-fact IS what you are looking for, you can just implement into your update like

UPDATE client cl,
      ( full select query from above ) as PreQualified
   set cl.parameter = "parameter1"
   where cl.id = PreQualified.id

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM