T-SQL,在进行多个连接和分组时连接字符串

时间:2016-10-07 16:11:59

标签: sql sql-server string tsql inner-join

在进行多重连接时,我不想连接组中的所有字符串。以下是我的方案

表格 - 日志

id | appname | level
---------------------
1  | app1    | debug
2  | app3    | warn
3  | app1    | debug
4  | app2    | info

表格 - log_tag

id | text
----------
1  | tag_wink
2  | tag_steve
3  | ignore
4  | jimmy

表格 - log_tag_map

tag_id | log_id
---------------
1      | 1
1      | 2
2      | 4
2      | 1
3      | 1

我的目标是;对于每个日志,请加入用逗号分隔的所有标记。

这是我想要的输出

id | appname | level | text
----------------------------
1  | app1    | debug | tag_wink, tag_steve, ignore
2  | app3    | warn  | tag_wink
3  | app1    | debug | 
4  | app2    | info  | tag_steve

我已经尝试了许多事情,但似乎无法获得所需的输出。

这为每个日志提供了一个tag_text

SELECT 
    log_table.id,
    log_table.appname,
    log_table.level,
    log_tag_table.text,
FROM log_table
INNER JOIN [log_tag_map] log_tag_map_table  
    ON log_tag_map_table.log_id = log_table.id
INNER JOIN [log_tag] log_tag_table 
    ON log_tag_map_table.tag_id = log_tag_table.id
ORDER BY log_table.id

在研究了T-SQL中的串联字符串后,我使用了着名的 FOR XML PATH 解决方案,但我似乎无法得到我想要的结果。我一直收到错误,说" log_tag_map_table.tag_id "是无效的,因为它不在聚合函数中。

SELECT 
    log_table.id,
    MAX(log_table.appname),
    MAX(log_table.level),
    STUFF((
        SELECT ', ' + [log_tag].text 
        FROM [log_tag] 
        WHERE
            log_tag_map_table.tag_id = log_tag.id AND 
            log_tag_map_table.log_id = log_table.id
        FOR XML PATH (''))
        , 1
        , 2
        , '')
FROM log_table
INNER JOIN [log_tag_map] log_tag_map_table  
    ON log_tag_map_table.log_id = log_table.id
INNER JOIN [log_tag] log_tag_table 
    ON log_tag_map_table.tag_id = log_tag_table.id
GROUP BY log_table.id
ORDER BY log_table.id

2 个答案:

答案 0 :(得分:2)

您可以通过在Group By中添加重命名列来解决此问题。查询中的Group by只是为了删除重复项。

我更喜欢APPLY运算符而不是correlated-sub-query。它看起来很简单,删除前导尾随 comma非常容易

SELECT DISTINCT log_table.id,
                log_table.appname,
                log_table.level,
                LEFT(concat_text, Len(concat_text) - 1) as Concat_text
FROM   log_table
       OUTER apply (SELECT [log_tag].text + ','
                    FROM   [log_tag_map] log_tag_map_table
                           JOIN log_tag
                             ON log_tag_map_table.tag_id = log_tag.id
                    WHERE  log_tag_map_table.log_id = log_table.id
                    FOR XML PATH ('')) oa (concat_text) 
ORDER BY log_table.id

演示:

架构设置

CREATE TABLE log_table
  (
     [id]      INT,
     [appname] VARCHAR(4),
     [level]   VARCHAR(5)
  );

CREATE TABLE log_tag
  (
     [id]   INT,
     [text] VARCHAR(9)
  );

CREATE TABLE log_tag_map
  (
     [tag_id] INT,
     [log_id] INT
  );

示例数据

INSERT INTO log_table
            ([id],[appname],[level])
VALUES      (1,'app1','debug'),
            (2,'app3','warn'),
            (3,'app1','debug'),
            (4,'app2','info');


INSERT INTO log_tag
            ([id],[text])
VALUES      (1,'tag_wink'),
            (2,'tag_steve'),
            (3,'ignore'),
            (4,'jimmy');



INSERT INTO log_tag_map
            ([tag_id],[log_id])
VALUES      (1,1),
            (1,2),
            (2,4),
            (2,1),
            (3,1);

查询:

SELECT DISTINCT log_table.id,
                log_table.appname,
                log_table.level,
                LEFT(concat_text, Len(concat_text) - 1) as Concat_Text
FROM   log_table
       OUTER apply (SELECT [log_tag].text + ','
                    FROM   [log_tag_map] log_tag_map_table
                           JOIN log_tag
                             ON log_tag_map_table.tag_id = log_tag.id
                    WHERE  log_tag_map_table.log_id = log_table.id
                    FOR XML PATH ('')) oa (concat_text) 
Order by log_table.id

Reuslt:

╔════╦═════════╦═══════╦═══════════════════════════╗
║ id ║ appname ║ level ║        Concat_Text        ║
╠════╬═════════╬═══════╬═══════════════════════════╣
║  1 ║ app1    ║ debug ║ tag_wink,tag_steve,ignore ║
║  2 ║ app3    ║ warn  ║ tag_wink                  ║
║  3 ║ app1    ║ debug ║ NULL                      ║
║  4 ║ app2    ║ info  ║ tag_steve                 ║
╚════╩═════════╩═══════╩═══════════════════════════╝

答案 1 :(得分:0)

这是使用CROSS APPLY的方式。

SELECT  Id ,appname,level,[text]
FROM    (
        SELECT   log_table.id,
        log_table.appname,
        log_table.level
        FROM    log_table
        ) a
CROSS APPLY
        (
        SELECT  CASE ROW_NUMBER() OVER(ORDER BY [log_tag].text) WHEN 1 THEN '' ELSE ', ' END +[log_tag].text [text()]
        FROM [log_tag_map] log_tag_map_table
                           JOIN log_tag
                             ON log_tag_map_table.tag_id = log_tag.id
                    WHERE  log_tag_map_table.log_id = a.id
        ORDER BY
                [log_tag].text
        FOR XML PATH ('')
        ) b([text])

输出:

enter image description here

您还可以使用request.post('http://service.com/upload').form({key:'value'}) 获取OP。

{{1}}

输出:

enter image description here