views:

19

answers:

2

I've got a sub-select in a query that looks something like this:

left outer join
  (select distinct ID from OTHER_TABLE) as MYJOIN
on BASE_OBJECT.ID = MYJOIN.ID

It's pretty straightforward. Checks to see if a certain relation exists between the main object being queried for and the object represented by OTHER_TABLE by whether or not MYJOIN.ID is null on the row in question.

But now the requirements have changed a little. There's another row in OTHER_TABLE that can have a value of 1 or 0, and the query needs to know whether a relation exists between the primary for a 1-value, and also if it exists for a 0 value. The obvious solutions is to put:

left outer join
  (select distinct ID, TYPE_VALUE from OTHER_TABLE) as MYJOIN
on BASE_OBJECT.ID = MYJOIN.ID

But that would be wrong because if 0-type and 1-type objects both exist for the same ID, it will increase the number of rows returned by the query, which isn't acceptable. So what I need is some sort of subselect that will return 1 row for each distinct ID, with a "1-type exists" column and a "0-type exists" column. And I have no idea how to code that in SQL.

For example, for the following table,

ID   | TYPE_VALUE
_________________
1    | 1
3    | 0
3    | 1
4    | 0

I'd like to see a result set like this:

ID   | HAS_TYPE_0 | HAS_TYPE_1
______________________________
1    | 0          | 1
3    | 1          | 1
4    | 1          | 0

Anyone know how I could set up a query to do this? Hopefully with a minimum of ugly hacks?

+3  A: 

In the general case, you would use EXISTS:

SELECT DISTINCT ID,
    CASE WHEN EXISTS (
            SELECT * FROM Table1 y 
            WHERE y.TYPE_VALUE = 0 AND ID = x.ID) 
        THEN 1 
        ELSE 0 END AS HAS_TYPE_0,
    CASE WHEN EXISTS (
            SELECT * FROM Table1 y
            WHERE y.TYPE_VALUE = 1 AND ID = x.ID) 
        THEN 1 
        ELSE 0 END AS HAS_TYPE_1
FROM Table1 x;

If you have a very large number of elements in the table, this won't perform so great - those nested subselects are often a kiss of death when it comes to performance.

For your specific case, you could also use GROUP BY and MAX() and MIN() to speed things up:

SELECT 
     ID,
     CASE WHEN MIN(TYPE_VALUE) = 0 THEN '1' ELSE 0 END AS HAS_TYPE_0,
     CASE WHEN MAX(TYPE_VALUE) = 1 THEN '1' ELSE 0 END AS HAS_TYPE_1
FROM Table1
GROUP BY ID;
Dave Markle
Thanks! The GROUP BY worked well. And yeah, this is likely to be a very large data set involving tens or hundreds of thousands of elements.
Mason Wheeler
+2  A: 

Instead of select distinct ID, TYPE_VALUE from OTHER_TABLE use

select ID,  
MAX(CASE WHEN TYPE_VALUE =0 THEN 1 END) as has_type_0,  
MAX(CASE WHEN TYPE_VALUE =1 THEN 1 END) as has_type_1  
from OTHER_TABLE
GROUP BY ID;

You can do the same using PIVOT opearator...

a1ex07