sql - MySQL - Join inside a correlated subquery -


i'm writing query assign users , respective domains ip addresses. no ip address may have duplicate users.

here's i've got far in sql fiddle: http://sqlfiddle.com/#!2/39c51/2/0

i have table contains (hundreds of thousands of) current assignments. example on smaller scale following:

mysql> select * test.usermap; +-------------+-------+-------------------+ | vip         | user  | domain            | +-------------+-------+-------------------+ | 100.50.20.1 | joe   | joesdomain.com    | | 100.50.20.1 | bob   | joesdomain.com    | | 100.50.20.2 | tom   | domain2.com       | | 100.50.20.2 | fred  | domain2.com       | | 100.50.20.2 | sally | domain2.com       | | 100.50.20.3 | admin | athriddomain.com  | | 100.50.20.4 | admin | numberfour.com    | | 100.50.20.3 | sally | fivewithsally.com | | 100.50.20.4 | jim   | thesix.com        | | 100.50.20.1 | admin | seven.com         | | 100.50.20.1 | sally | seven.com         | | 100.50.20.1 | sue   | seven.com         | | 100.50.20.5 |       |                   | | 100.50.20.6 |       |                   | +-------------+-------+-------------------+ 14 rows in set (0.00 sec) 

i have table, contains yet-to-be assigned users, again, small scale example:

mysql> select * test.newusers; +-------+-----------+ | user  | domain    | +-------+-----------+ | jim   | eight.com | | sally | eight.com | | admin | nine.com  | | james | ten.com   | | jane  | ten.com   | +-------+-----------+ 5 rows in set (0.00 sec) 

the idea here assign users under eight.com .5 because thats earliest ip has neither 'jim' nor 'sally', , nine.com .2 , ten.com .1 because of respective user conflicts (or lack thereof).

the result i'm looking like:

+-------------+-------+-----------+ | vip         | user  | domain    | +-------------+-------+-----------+ | 100.50.20.1 | james | ten.com   | | 100.50.20.1 | jane  | ten.com   | | 100.50.20.2 | admin | nine.com  | | 100.50.20.5 | jim   | eight.com | | 100.50.20.5 | sally | eight.com | +-------------+-------+-----------+ 5 rows in set (0.01 sec) 

i can subquery inside correlated subquery, so:

mysql> select   (     select vip      test.usermap     vip not in     (         select distinct vip          test.usermap           user in         (             select user              test.newusers              domain = n.domain         )     )     order inet_aton(vip) asc     limit 1 ) vip, n.user, n.domain  test.newusers n order inet_aton(vip) asc; +-------------+-------+-----------+ | vip         | user  | domain    | +-------------+-------+-----------+ | 100.50.20.1 | james | ten.com   | | 100.50.20.1 | jane  | ten.com   | | 100.50.20.2 | admin | nine.com  | | 100.50.20.5 | jim   | eight.com | | 100.50.20.5 | sally | eight.com | +-------------+-------+-----------+ 5 rows in set (0.00 sec) 

but horrendously inefficient, , production mapping , newusers tables 300k , 50k rows respectively, out of question.

i'm trying make more efficient using joins instead of nested subqueries, replaced inner query join , listed outer query's columns in on clause, seems isn't possible:

mysql> select  (     select distinct vip      test.usermap u      join test.newusers r         on r.domain = n.domain         , r.user != u.user     order inet_aton(vip) asc limit 1 ) vip, n.user, n.domain test.newusers n; error 1054 (42s22): unknown column 'n.domain' in 'on clause' mysql>  

while logic of query makes sense, because replacing outer query reference string constant represent works fine:

mysql> select (     select distinct vip      test.usermap u      join test.newusers r         on r.domain = 'ten.com'         , r.user != u.user     order inet_aton(vip) asc limit 1 ) vip, n.user, n.domain test.newusers n domain = 'ten.com'; +-------------+-------+---------+ | vip         | user  | domain  | +-------------+-------+---------+ | 100.50.20.1 | james | ten.com | | 100.50.20.1 | jane  | ten.com | +-------------+-------+---------+ 2 rows in set (0.00 sec) 

my question is: there way reference column outer query inside of join on inner query? if not, kind of (if any) alternatives exist without nesting subqueries in inefficient manner?

again, have fiddle here: http://sqlfiddle.com/#!2/39c51/2/0

i not sure how much, if any, more efficient be, query can rewritten without nesting multiple subqueries:

select  inet_ntoa(min(inet_aton(usermap.vip))) vip,         newusers.user,          newusers.domain    newusers         cross join usermap         left join         (   select  u.domain, m.vip                newusers u                     inner join usermap m                         on u.user = m.user         ) ex             on ex.domain = newusers.domain             , ex.vip = usermap.vip   ex.domain null group newusers.user, newusers.domain order vip asc;    

example on sql fiddle

addendum

the query above not return rows there no vip available, e.g. if 100.50.20.5 , 100.50.20.1 removed usermap query return:

vip             user    domain null            jim     eight.com null            sally   eight.com 100.50.20.1     james   ten.com 100.50.20.1     jane    ten.com 100.50.20.2     admin   nine.com 

whereas query have written return rows vip not null:

vip             user    domain 100.50.20.1     james   ten.com 100.50.20.1     jane    ten.com 100.50.20.2     admin   nine.com 

to around can use union:

select  inet_ntoa(min(inet_aton(a.vip))) vip,         a.user,          a.domain    (   select  usermap.vip,                     newusers.user,                      newusers.domain                newusers                     cross join usermap                     left join                     (   select  u.domain, m.vip                            newusers u                                 inner join usermap m                                     on u.user = m.user                     ) ex                         on ex.domain = newusers.domain                         , ex.vip = usermap.vip               ex.domain null             union             select  null vip,                     newusers.user,                     newusers.domain                newusers         ) group a.user, a.domain order vip asc; 

revised example on sql fiddle

i not sure logic handling cases there no vip available can't suggest solution part. can next vip using this:

select  inet_ntoa(max(inet_aton(usermap.vip)) + 1) nextvip    usermap 

another issue problem collisions in newusers, e.g. if newusers table contained these records:

('jim','eight.com'), ('sally','eight.com'), ('jim','eleven.com'), ('sally','eleven.com'); 

both query , mine allocate of these vip 100.50.20.5. if happen think best way around insert user names 1 domain @ 1 time. can done using joins:

to simplify query have created 2 views

create view usedvip     select  u.domain, m.vip        newusers u             inner join usermap m                 on u.user = m.user;  create view newusermap      select  usermap.vip,             newusers.user,              newusers.domain        newusers             cross join usermap             left join usedvip ex                 on ex.domain = newusers.domain                 , ex.vip = usermap.vip       ex.domain null; 

and final query is:

select  inet_ntoa(min(inet_aton(a.vip))) vip,         a.user,          a.domain    newusermap         left join newusermap b             on a.user = b.user             , a.vip = b.vip             , a.domain > b.domain         left join newusermap c             on a.user = c.user             , b.domain = c.domain             , b.vip < c.vip   c.user null group a.user, a.domain order vip asc; 

which returns:

vip             user    domain 100.50.20.1     jane    ten.com 100.50.20.1     james   ten.com 100.50.20.2     admin   nine.com 100.50.20.5     sally   eight.com 100.50.20.5     jim     eight.com 100.50.20.6     jim     eleven.com 100.50.20.6     sally   eleven.com 

example on sql fiddle


Comments