tags:

views:

156

answers:

4

I don't even know what the name of my problem is called, so I'm just gonna put some sample data. I don't mind fuzzy results on this (this is the best way I can think to express it. I don't mind if I overlook some data, this is for approximated evaluation, not for detailed accounting, if that makes sense). But I do need every record in TABLE 1, and I would like to avoid the nulls case indicated below.

IS THIS POSSIBLE?

TABLE 1
acctnum sub fname  lname  phone
 12345   1   john   doe   xxx-xxx-xxxx
 12346   0   jane   doe   xxx-xxx-xxxx
 12347   0   rob    roy   xxx-xxx-xxxx
 12348   0   paul  smith  xxx-xxx-xxxx

TABLE 2
acctnum sub division
 12345   1   EAST
 12345   2   WEST
 12345   3   NORTH
 12346   1   TOP
 12346   2   BOTTOM
 12347   2   BALLOON
 12348   1   NORTH

So if we do a "regular outer" join, we'd get some results like this, since the sub 0's don't match the second table:

TABLE AFTER JOIN
acctnum sub fname  lname  phone         division
 12345   1   john   doe   xxx-xxx-xxxx   EAST
 12346   0   jane   doe   xxx-xxx-xxxx   null
 12347   0   rob    roy   xxx-xxx-xxxx   null
 12348   0   paul  smith  xxx-xxx-xxxx   null

But I would rather get

TABLE AFTER JOIN
acctnum sub fname  lname  phone         division
 12345   1   john   doe   xxx-xxx-xxxx   EAST
 12346   0   jane   doe   xxx-xxx-xxxx   TOP
 12347   0   rob    roy   xxx-xxx-xxxx   BALLOON
 12348   0   paul  smith  xxx-xxx-xxxx   NORTH

And I'm trying to avoid:

TABLE AFTER JOIN
acctnum sub fname  lname  phone         division
 12345   1   john   doe   xxx-xxx-xxxx   EAST
 12345   1   john   doe   xxx-xxx-xxxx   WEST
 12345   1   john   doe   xxx-xxx-xxxx   NORTH
 12346   0   jane   doe   xxx-xxx-xxxx   TOP
 12346   0   jane   doe   xxx-xxx-xxxx   BOTTOM
 12347   0   rob    roy   xxx-xxx-xxxx   BALOON
 12348   0   paul  smith  xxx-xxx-xxxx   NORTH

So I decided to go with using a union and two if conditions. I'll accept a null for conditions where the sub account is defined in table 1 but not in table 2, and for everything else, I'll just match against the min.

+3  A: 

If I'm understanding correctly, it looks like you're trying to join on the sub column if it matches. If there's no match on sub, then you want it to select the "first" row for that acctnum. Is this correct?

If so, you'll need to left join on the full match, then perform another left join on a select statement that determines the division that corresponds to the lowest sub value for that acctnum. The row_number() function can help you with this, like this:

select
    t1.acctnum, 
    t1.sub, 
    t1.fname, 
    t1.lname, 
    t1.phone, 
    isnull(t2_match.division, t2_first.division) as division

from table1 t1

left join table2 t2_match on t2_match.acctnum = t1.acctnum and t2_match.sub = t1.sub
left join 
(
    select 
        acctnum, 
        sub, 
        division,
        row_number() over (partition by acctnum order by sub) as rownum

    from table2
) t2_first on t2_first.acctnum = t1.acctnum

EDIT

If you don't care at all about which record you get back from table 2 when a matching sub doesn't exist, you could combine two different queries (one that matches the sub and one that just takes the min or max division) with a union.

select
    t1.acctnum, 
    t1.sub, 
    t1.fname, 
    t1.lname, 
    t1.phone, 
    t2.division

from table1 t1

join table2 t2 on t2.acctnum = t1.acctnum and t2.sub = t1.sub

union

select
    t1.acctnum, 
    t1.sub, 
    t1.fname, 
    t1.lname, 
    t1.phone, 
    min(t2.division)

from table1 t1

join table2 t2 on t2.acctnum = t1.acctnum
left join table2 t2_match on t2_match.acctnum = t1.acctnum and t2_match.sub = t1.sub

where t2_match.acctnum is null

Personally, I don't find the union syntax any more compelling and you now have to maintain the query in two places. For this reason, I'd favor the row_number() approach.

Adam Robinson
Thanks, I was trying to avoid a rownumber situation, figured there was a slightly more elegant way to solve this particular problem. I think @Ben S has the way for me to go, gonna look into that. Otherwise, I guess this was the only other way to solve it.
drachenstern
@drachenstern: I've edited my answer to add an alternative that uses a `union` instead of `row_number()`, but I still prefer the original solution.
Adam Robinson
@Adam Robinson ~ Yeah, the rownumber will be easiest, just gonna toss `TABLE 2` into a tablevar for easier maintenance, and join against the tablevar with the one division per acct, and let all results be fuzzy. We're talking 400 records, so it's not gonna be upsetting to have one division be off by a few against another. If they want it right, they'll clean up the sub's and then I'll make it exact. Thanks again.
drachenstern
@drachenstern: You might want to consider a common table expression instead of a table variable. http://msdn.microsoft.com/en-us/magazine/cc163346.aspx
Adam Robinson
+1  A: 

This will give your desired result, exactly (for the shown data):

Updated to not assume there is always a sub==1 value:

SELECT
    T1.acctnum,
    T1.sub,
    T1.fname,
    T1.lname,
    T1.phone,
    T2.division
FROM
    TABLE_1 T1
LEFT JOIN
    TABLE_2 T2  ON T1.acctnum = T2.acctnum
AND
    T2.sub  = (SELECT MIN(T3.sub) FROM TABLE_2 T3  WHERE T1.acctnum = T3.acctnum)
ORDER BY
    T1.lname,
    T1.fname,
    T1.acctnum
Brock Adams
Can't be sure that T2.sub will include a 1 for a given entry. T2.sub may start at a 2, etc.
drachenstern
@drachenstern: Well, then clean your data! :) If T2.sub is not well behaved, then one of the other answers will probably work. I'll revisit or delete my answer in several hours.
Brock Adams
@Brock: You may be making unfounded assumptions about the nature of the data. Do you have the knowledge about what the data represents to be able to say that `sub` *should* always start at 1? You certainly may be right, but the information in the question doesn't directly support this.
Adam Robinson
@Adam Robinson: I did make an assumption but it was a logical guess based on the OP's presented data -- which shows that each acct has a `table_2.sub == 1`, *AND* that this corresponded to his desired value. The OP set me straight and I'll be updating my answer in a minute.
Brock Adams
@Brock Adams ~ "set me straight" sounds so odd ... ;) ~ Also, I made sure my initial data set indicated a condition where there was no T2.sub '1' for a particular data point. Yeah, it's a contrived data set, so is almost any example. But it simplistically matches reality in this case. ~ Also, as for "clean the data" ~ Not my option, I can only match what exists, ya know? I even offered to knock up a script that'll do it's best to match based on name and address, but was told to not do that, if the customer is concerned, let them cut another work order ;)
drachenstern
@drachenstern: re `I made sure my initial data set indicated a condition where there was no T2.sub '1' for a particular data point.` Oops, so you did. My bad. Usually I test the answers on the OP's data, but I was too distracted this go round (actually have work to do on occasion). You set me straight, **again**. ;-) PS. that's not a bad phrase where I come from. Finally, I tried to signal, with that smilie, that I was joking about cleaning your data.This current solution should also work.
Brock Adams
@Brock Adams ~ lol, I know what you mean. Thanks for the input on the problem!
drachenstern
A: 

It may also work for you:

SELECT  t1.acctnum, t1.sub, t1.fname, t1.lname, t1.phone, 
ISNULL(MAX(t2.division),MAX(t3.division)) as division
FROM table_1 t1
LEFT JOIN table_2 t2 ON (t2.acctnum = t1.acctnum AND t1.sub = t2.sub)
LEFT JOIN table_2 t3 ON (t3.acctnum = t1.acctnum)
GROUP BY  t1.acctnum, t1.sub, t1.fname, t1.lname, t1.phone
a1ex07
+2  A: 

try to use

SELECT     MIN(Table_1.acctnum) as acctnum , MIN(Table_1.sub) as sub,MIN( Table_1.fname) as fname, MIN(Table_1.lname) as name, MIN(Table_1.phone) as phone, MIN(Table_2.division) as division 
FROM Table_1 INNER JOIN   Table_2 ON Table_1.acctnum = Table_2.acctnum AND Table_1.sub = Table_2.sub
where Table_1.sub>0
group by Table_1.acctnum 
union 
SELECT     MIN(Table_1.acctnum) as acctnum , MIN(Table_1.sub) as sub,MIN( Table_1.fname) as fname, MIN(Table_1.lname) as name, MIN(Table_1.phone) as phone, MIN(Table_2.division) as division 
FROM Table_1 INNER JOIN   Table_2 ON Table_1.acctnum = Table_2.acctnum 
where Table_1.sub=0
group by Table_1.acctnum

this is the result

12345   1   john        doe         xxxxxxxxxx  EAST      
12346   0   jane        doe         xxxxxxxxxx  BOTTOM    
12347   0   rob         roy         xxxxxxxxxx  BALLOON   
12348   0   paul        smith       xxxxxxxxxx  NORTH  

if you change min to max TOP will be insted of BOTTOM on the second row

Waleed A.K.
Properly formatted that answer looks a lot better than it does right now. It took me several moments to figure out what you were suggesting.
drachenstern
@drachenstern: Sorry about that, you are right I'll try to improve my writing skill in future.
Waleed A.K.
@Waleed A.K. ~ I just meant, don't run it all together on a few lines, feel free to use the carriage returns in the code section. Practice with the SO editor, it's pretty helpful on posting code. But once I grokked the code, it was the simplest way. I liked it.
drachenstern