views:

136

answers:

3

I have a table like the following which is basically used to "give a name" to a value in a table (this table contains values for a bunch of other tables as well, not just for MYTABLE; I've omitted a few irrelevant fields from NAMEVALUEMAP):

NAMEVALUEMAP Table
---------------------
VALUE_  | NAME_
---------------------
0       | ZERO
1       | ONE

I didn't want to use JOINs so I thought of using Sub-Queries.

Problem is when a value does not exist in the NAMEVALUEMAP table then NULL is shown.
Instead of NULL I want to show the actual value from MYTABLE (MYTABLE has ID field as identity column and contains a few rows):

-- //Fine, prints word 'ZERO' when MYTABLE.ABC is 0
SELECT 
(SELECT NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = (SELECT ABC FROM MYTABLE inner_ WHERE inner_.ID = outer_.ID))
FROM 
MYTABLE outer_

-- //Not Fine, prints NULL (because "999" is not in NAMEVALUEMAP). In this case, MYTABLE.ABC is 999
-- //Want it to print 999 if the value is not in NAMEVALUEMAP
SELECT 
(SELECT NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = (SELECT ABC FROM MYTABLE inner_ WHERE inner_.ID = outer_.ID))
FROM 
MYTABLE outer_

-- //Tried COALESCE, but the error is "Invalid column name 'VALUE_'"
SELECT 
COALESCE((SELECT NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = (SELECT ABC FROM MYTABLE inner_ WHERE inner_.ID = outer_.ID)), ABC)
FROM 
MYTABLE outer_

Also, is there a better way to do this sort of value-to-name mapping?

+1  A: 

It is a left join, unless you want soem EXISTS/UNION construct. Not tested:

SELECT
    COALESCE(N.VALUE, M.ABC)
FROM
    MYTABLE M
    LEFT JOIN
    NAMEVALUEMAP N ON M.VALUE N.ABC

If you really want to avoid JOINs...

SELECT
    ABC
FROM
    MYTABLE M
WHERE
    NOT EXISTS (SELECT * FROM NAMEVALUEMAP N WHERE M.VALUE N.ABC)
UNION ALL
SELECT
    VALUE
FROM
    NAMEVALUEMAP N
WHERE
    EXISTS (SELECT * FROM MYTABLE M WHERE M.VALUE N.ABC)

Edit:

The SELECT *, 1 or NULL in EXISTS question again

gbn
I'm able to do this with JOINs but I want to avoid doing JOINs if I can. Please also see me reply to comment by astander on my reasoning for not using JOINs. I hope i'm right about that?
Liao
select * can be a bit expensive for an exists in some DBMS, I would normally use select 1 instead
Steve De Caux
Database engines are designed to JOIN. A subquery will most likely generate more IO and a worse plan because it's a subquery for each column. One join = gets all columns.
gbn
@Steve De Caux: it's not evaluated. Try `EXISTS (SELECT 1/0...)`. It hasn't been evaluated on SQL Server since at least SQL Server 2000.
gbn
@Steve De Caux: Mentioned in ANSI SQL 1992 Standard too, page 191 http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
gbn
Fair comment, old habits die hard (I mean old)
Steve De Caux
Well I think I will be using JOINs.. :) But one thing to note is that in my case, ISNULL() is the option that will work, and not COALESCE(). Thats because NAMEVALUEMAP.NAME_ is varchar and the value from the MYTABLE is an INT. Thusly I get conversion error "Conversion failed when converting the varchar value 'One' to data type tinyint." Thank you
Liao
ISNULL is SQL Server specific and takes the 1st datatype. COALESCE is ANSI standard and works a bit differently. I'd use ISNULL
gbn
+1  A: 

I would recomend using a LEFT JOIN (is there any reason you are voidung it?) and ISNULL

SELECT  ISNULL(NAME_, ABC)
FROM    MYTABLE m LEFT JOIN
    NAMEVALUEMAP n ON m.ABC = n.VALUE_

Well, in that case you can try

SELECT  ISNULL((select NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = m.ABC), m.ABC)
FROM    MYTABLE m
astander
There are 3 such "MYTABLEs" I will be selecting about 20 columns from, each of those columns need this mapping. That would become quite a complicated (looking) query, plus (I'm not sure) I suppose sub-queries will be faster than JOINs?
Liao
No, joins will be faster than sub-queries.
David M
Have a look at http://www.eggheadcafe.com/software/aspnet/32705705/join-vs-subquery.aspx *In general there is no reason to assume that a subquery will be fasteror slower than a join. Specific cases can point one way or the other,but there are too many variables for a general answer.*
astander
Perfect, Guruji. I shall be using the JOIN as it seems to have a better execution plan. Will come to know more in usage. Thanks!
Liao
A: 

EDIT:

SELECT 
COALESCE(
   (SELECT NAME_ FROM NAMEVALUEMAP WHERE VALUE_ = 
      (SELECT ABC FROM MYINNERTABLE inner_ WHERE inner_.ID = outer_.ID)
   ),
   <int to string>(
      SELECT ABC FROM MYINNERTABLE inner_ WHERE inner_.ID = outer_.ID
   )
)

FROM 
MYTABLE outer_

where column function <int to string> is appropriate for sqlserver. In mysql it would be CAST(). Without conversion, the query will throw a wobbly about the mismatched datatypes.

Steve De Caux
I don't think that would work for me - the value to be mapped to a name comes from a different table..
Liao
In your question you used the same table name (MYTABLE) for inner_ and outer_ => I assumed it was the same table. I've edited to accomodate your comment
Steve De Caux