COALESCE function won't return CHAR(1)

时间:2018-07-24 10:24:17

标签: sql sql-server coalesce

Using COALESCE function but getting the following error:

Conversion failed when converting the varchar value 'X' to data type int.

I have to join two tables on two conditions. I want that if the second condition doesn't hold but there is a blank cell (not null but blank '') in Table 1 then to join to that row. If the second condition doesn't hold then to return a zero.

Join Table 1 and Table 2 - return Table 2 and column 3 from Table 1.

Table 1

(A, 1, X),
(A, 2, Y),
(A, 3, Z),
(A, , X),
(B, 1, X),
(B, 2, Z),
(B, 3, Y),

Table 2

(A, 1),
(A, 2),
(A, 3),
(A, 5),
(B, 1),
(B, 2),
(B, 3),
(B, 5)

I want to get a return of

(A, 1, X),
(A, 2, Y),
(A, 3, Z),
(A, 5, X),
(B, 1, X),
(B, 2, Z),
(B, 3, Y),
(B, 5, NULL)

Code:

DECLARE @table1 TABLE (letter1 CHAR(1), num1 INT, letter2 CHAR(1))  
DECLARE @table2 TABLE (letter1 CHAR(1), num1 INT)  

INSERT INTO @table1 VALUES    
('A', 1, 'X'),  
('A', 2, 'Y'),  
('A', 3, 'Z'),  
('A', null, 'X'),  
('B', 1, 'X'),  
('B', 2, 'Y'),  
('B', 3, 'Z')  

INSERT INTO @table2 VALUES 
('A', 1),  
('A', 2),  
('A', 3),  
('A', 5),  
('B', 1),  
('B', 2),    
('B', 3),  
('B', 5) 

SELECT t2.*,   
    COALESCE(  
        (SELECT TOP 1 letter2 FROM @table1 WHERE letter1 = t2.letter1 AND num1 = t2.num1),   
        (SELECT TOP 1 letter2 FROM @table1 WHERE letter1 = t2.letter1 AND num1 IS NULL),   
        0  
    ) AS missing_letter  
FROM @table2 t2  

5 个答案:

答案 0 :(得分:2)

也许您需要:

select t1.*, t2.*
from table1 t1 outer apply
    ( select top (1) t2.*
      from table2 t2
      where t1.col1 = t.col1 and t1.col2 in ('', t2.col2)
      order by t2.col2 desc
    ) t2;

答案 1 :(得分:2)

如果我理解正确,则与coalesce()的关系较小,而与join的关系较大:

select t2.*, coalesce(t1.letter2, t1def.letter2) as letter2
from table2 t2 left join
     table1 t1
     on t2.letter1 = t1.letter1 and t2.num1 = t1.num1 left join
     table1 t1def
     on t2.letter1 = t1def.letter1 and t1def.num1 is null;

答案 2 :(得分:2)

这里的问题是您的数据类型。 COALESCECASE表达式的简称。例如。 COALESCE('a',1,'c')是以下方面的简称:

CASE WHEN 'a' IS NOT NULL THEN 'a'
     WHEN 1 IS NOT NULL THEN 1
     ELSE 'c'
END

文档(COALESCE (Transact-SQL)也对此进行了描述:

  

COALESCE表达式是CASE的语法快捷方式   表达。也就是说,代码COALESCE(expression1,...n)是   查询优化器将其重写为以下CASE表达式:

CASE  
WHEN (expression1 IS NOT NULL) THEN expression1  
WHEN (expression2 IS NOT NULL) THEN expression2  
...  
ELSE expressionN  
END

CASE表达式紧跟Data type precedence,并且int的数据类型优先级比varchar高。因此,所有内容都会隐式转换为int。这就是COALESCECASE表达式都将失败的原因,因为'a''c'都不能转换为int

因此,您需要将CONVERT显式int变成varchar

COALESCE('a',CONVERT(char(1),1),'c')

但是,文档(如上引用)也陈述如下:

  

这表示输入值(expression1,expression2,   expressionN等)被多次评估。另外,遵守   在SQL标准中,包含子查询的值表达式为   被认为是不确定的,子查询将被评估两次。在   无论哪种情况,在第一个之间可以返回不同的结果   评价和后续评价。

     

例如,执行代码COALESCE((subquery), 1)时,   子查询被评估两次。结果,您可以得到与众不同   结果取决于查询的隔离级别。例如,   代码可以在NULL隔离级别下返回READ COMMITTED   多用户环境。为确保返回稳定的结果,请使用   SNAPSHOT ISOLATION隔离级别,或将COALESCE替换为   ISNULL功能。

考虑到您正在使用子查询,此处(嵌套)ISNULL可能是更好的选择。

值得注意的是,由于功能相似,人们似乎对它们感到困惑,但是COALESCEISNULL的行为并不相同。 COALESCE使用数据类型优先级,但是,ISNULL隐式将第二个值强制转换为第一个参数的数据类型。因此ISNULL('a',1)可以正常工作,但是COALESCE('a',1)不能正常工作。

答案 3 :(得分:1)

只需将零更改为null。您不能在合并中混合数据类型:

SELECT t2.*,
COALESCE(
(SELECT TOP 1 letter2 FROM @table1 WHERE letter1 = t2.letter1 AND num1 = t2.num1),
(SELECT TOP 1 letter2 FROM @table1 WHERE letter1 = t2.letter1 AND num1 IS NULL),
null
) AS missing_letter
FROM @table2 t2

答案 4 :(得分:0)

如果COALESCE中的0替换为'0',则该查询有效。
这样,COALESCE不包含混合数据类型。

SELECT t2.*,
    COALESCE(  
        (SELECT TOP 1 letter2 FROM @table1 t1 WHERE t1.letter1 = t2.letter1 AND t1.num1 = t2.num1),   
        (SELECT TOP 1 letter2 FROM @table1 t1 WHERE t1.letter1 = t2.letter1 AND t1.num1 IS NULL),   
        '0'
    ) AS missing_letter  
FROM @table2 t2
ORDER BY t2.letter1, t2.num1;

您可以避免两次从table1检索数据。
通过使用外部申请。

由于预期结果的('B',5)为NULL,所以甚至不需要这种方式。

SELECT t2.letter1, t2.num1, t1.letter2 AS missing_letter
FROM @table2 AS t2
OUTER APPLY (
   select top 1 t.letter2
   from @table1 AS t
   where t.letter1 = t2.letter1 
     and (t.num1 is null or t.num1 = t2.num1)
   order by t.num1 desc
) AS t1
ORDER BY t2.letter1, t2.num1;

结果:

letter1 num1    missing_letter
------- ----    --------------
A       1       X
A       2       Y
A       3       Z
A       5       X
B       1       X
B       2       Y
B       3       Z
B       5       NULL