SQL Server如何仅拼接非空列?CASE WHEN实现效果不佳
问题
在SQL Server中,有没有办法只拼接非空的列?我试过用CASE WHEN实现,但过程太繁琐,不想在字段为空或NULL时,把包含“名称+字段+', '”的整段内容都拼进去。比如:
'Sub-título: ', p.[config_metadata subtitle],', ',
这段对应的字段不为空时就保留,但如果是:
'Descrição: ', p.[last_timeline descricao],', ',
对应的字段为NULL,就完全不拼接这段内容。
期望输出示例:如果第一、第三段对应的字段非空,第二段为空,结果应该是:
'Sub-título: field content, Fwd_descrição: field content, '
我当前用CONCAT函数的SQL语句如下:
SELECT p._id AS id_documento, '16' AS id_fluxo, p.nP AS np, t.evento, YEAR(created_at) AS ano_doc, p.[config_metadata title] AS assunto, CONCAT ('Sub-título: ', p.[config_metadata subtitle],', ', 'Descrição: ', p.[last_timeline descricao],', ', 'Fwd_descrição: ', p.[last_timeline forwardingDescription],', ', 'Justificativa: ', p.[last_version Justificativa],', ', 'Motivo: ', p.[last_version motivo],', ', 'Área propriedade: ', p.[last_version areapropriedade],', ', 'Endereço: ', p.[last_version endereco],', ', 'Atividade: ', p.[last_version atividade],', ', 'Atividade empreendida: ', p.[last_version atividadeempreend],', ', 'Descreva finalidade: ', p.[last_version descrevafinalidade],', ', 'Nome: ', p.[last_version nome],', ', 'Nome empreendimento: ', [last_version nomeempreendimento],', ', 'Quantidade: ', [last_version quantidade],', ', 'Outros: ', [last_version outros],', ', 'Data finalizado: ', p.[finalizado date] ) AS conteudo, '' as codigo, '0' as prioridade, t.from_id, t.from_nome, t.to_id, t.to_nome, CONVERT(DATE,[created_at]) AS dia, CONVERT(TIME(0), [created_at]) AS hora, created_at FROM my_table AS p JOIN my_table2 AS t ON p._id = t.pid WHERE t.evento = 'Processo criado'
使用的SQL Server版本:
Microsoft SQL Server 2022 (RTM-GDR) (KB5021522) - 16.0.1050.5 (X64) Jan 23 2023 17:02:42 Copyright (C) 2022 Microsoft Corporation Developer Edition (64-bit) on Windows 10 Home Single Language 10.0 <X64> (Build 22621: ) (Hypervisor)
解决方案
针对SQL Server 2022,有两种简洁的方法可以实现只拼接非空字段的需求:
方法1:使用CONCAT_WS + NULLIF
利用CONCAT_WS自动忽略NULL值的特性,结合NULLIF把空字符串转为NULL,就能跳过空字段的拼接:
SELECT p._id AS id_documento, '16' AS id_fluxo, p.nP AS np, t.evento, YEAR(created_at) AS ano_doc, p.[config_metadata title] AS assunto, CONCAT_WS(', ', NULLIF('Sub-título: ' + p.[config_metadata subtitle], 'Sub-título: '), NULLIF('Descrição: ' + p.[last_timeline descricao], 'Descrição: '), NULLIF('Fwd_descrição: ' + p.[last_timeline forwardingDescription], 'Fwd_descrição: '), NULLIF('Justificativa: ' + p.[last_version Justificativa], 'Justificativa: '), NULLIF('Motivo: ' + p.[last_version motivo], 'Motivo: '), NULLIF('Área propriedade: ' + p.[last_version areapropriedade], 'Área propriedade: '), NULLIF('Endereço: ' + p.[last_version endereco], 'Endereço: '), NULLIF('Atividade: ' + p.[last_version atividade], 'Atividade: '), NULLIF('Atividade empreendida: ' + p.[last_version atividadeempreend], 'Atividade empreendida: '), NULLIF('Descreva finalidade: ' + p.[last_version descrevafinalidade], 'Descreva finalidade: '), NULLIF('Nome: ' + p.[last_version nome], 'Nome: '), NULLIF('Nome empreendimento: ' + [last_version nomeempreendimento], 'Nome empreendimento: '), NULLIF('Quantidade: ' + [last_version quantidade], 'Quantidade: '), NULLIF('Outros: ' + [last_version outros], 'Outros: '), NULLIF('Data finalizado: ' + CONVERT(VARCHAR, p.[finalizado date]), 'Data finalizado: ') ) AS conteudo, '' as codigo, '0' as prioridade, t.from_id, t.from_nome, t.to_id, t.to_nome, CONVERT(DATE,[created_at]) AS dia, CONVERT(TIME(0), [created_at]) AS hora, created_at FROM my_table AS p JOIN my_table2 AS t ON p._id = t.pid WHERE t.evento = 'Processo criado'
逻辑说明:
- 当字段为空时,
'名称: ' + 空字段的结果就是'名称: ',此时NULLIF会返回NULL CONCAT_WS会自动忽略所有NULL参数,只拼接非NULL的部分,并用指定的,作为分隔符
方法2:使用STRING_AGG + VALUES子句
这种方法更灵活,把每个字段和对应的标签组成临时行,过滤空值后再聚合拼接:
SELECT p._id AS id_documento, '16' AS id_fluxo, p.nP AS np, t.evento, YEAR(created_at) AS ano_doc, p.[config_metadata title] AS assunto, ( SELECT STRING_AGG(CAST(item AS VARCHAR(MAX)), ', ') FROM (VALUES ('Sub-título: ' + p.[config_metadata subtitle]), ('Descrição: ' + p.[last_timeline descricao]), ('Fwd_descrição: ' + p.[last_timeline forwardingDescription]), ('Justificativa: ' + p.[last_version Justificativa]), ('Motivo: ' + p.[last_version motivo]), ('Área propriedade: ' + p.[last_version areapropriedade]), ('Endereço: ' + p.[last_version endereco]), ('Atividade: ' + p.[last_version atividade]), ('Atividade empreendida: ' + p.[last_version atividadeempreend]), ('Descreva finalidade: ' + p.[last_version descrevafinalidade]), ('Nome: ' + p.[last_version nome]), ('Nome empreendimento: ' + [last_version nomeempreendimento]), ('Quantidade: ' + [last_version quantidade]), ('Outros: ' + [last_version outros]), ('Data finalizado: ' + CONVERT(VARCHAR, p.[finalizado date])) ) AS temp(item) WHERE item IS NOT NULL AND NOT item LIKE '%: ' ) AS conteudo, '' as codigo, '0' as prioridade, t.from_id, t.from_nome, t.to_id, t.to_nome, CONVERT(DATE,[created_at]) AS dia, CONVERT(TIME(0), [created_at]) AS hora, created_at FROM my_table AS p JOIN my_table2 AS t ON p._id = t.pid WHERE t.evento = 'Processo criado'
逻辑说明:
VALUES子句把每个标签+字段的组合转为临时行数据WHERE子句过滤掉NULL值,以及仅含标签的无效内容(用NOT item LIKE '%: '统一匹配所有空标签)STRING_AGG把剩下的有效内容用,拼接成最终字符串
两种方法都能替代繁琐的CASE WHEN写法,其中方法1更简洁,更适配你的场景。
内容的提问来源于stack exchange,提问作者Vinícius Sodré Quadros
相关产品推荐
相关产品推荐

