对于一个班级项目,我和其他一些人决定制作一个(非常丑陋的)StackOverflow受限克隆。 为此,我们正在处理一个查询:

主页:列出所有问题,其得分(从投票中计算)以及与他们的第一个修订版相对应的用户,以及答案的数量,这些答案按照对问题的最后操作(以操作为准,以降序排列)答案,答案的编辑或问题的编辑)。

现在,我们已经弄清楚了所有事情,除了如何表示问题标签。 我们目前正在使用标记的MN映射到类似这样的问题:

CREATE TABLE QuestionRevisions (
id INT IDENTITY NOT NULL,
question INT NOT NULL,
postDate DATETIME NOT NULL,
contents NTEXT NOT NULL,
creatingUser INT NOT NULL,
title NVARCHAR(200) NOT NULL,
PRIMARY KEY (id),
CONSTRAINT questionrev_fk_users FOREIGN KEY (creatingUser) REFERENCES
Users (id) ON DELETE CASCADE,
CONSTRAINT questionref_fk_questions FOREIGN KEY (question) REFERENCES
Questions (id) ON DELETE CASCADE
);

CREATE TABLE Tags (
id INT IDENTITY NOT NULL,
name NVARCHAR(45) NOT NULL,
PRIMARY KEY (id)
);

CREATE TABLE QuestionTags (
tag INT NOT NULL,
question INT NOT NULL,
PRIMARY KEY (tag, question),
CONSTRAINT qtags_fk_tags FOREIGN KEY (tag) REFERENCES Tags(id) ON
DELETE CASCADE,
CONSTRAINT qtags_fk_q FOREIGN KEY (question) REFERENCES Questions(id) ON
DELETE CASCADE
);

现在,对于此查询,如果我们只是加入QuestionTags,那么我们将一遍又一遍地获得问题和标题。 如果不这样做,那么我们有N个查询场景,同样糟糕。 理想情况下,我们将在结果行中包含以下内容:

+-------------+------------------+
| Other Stuff | Tags             |
+-------------+------------------+
| Blah Blah   | TagA, TagB, TagC |
+-------------+------------------+

基本上-对于JOIN中的每一行,在结果标签上进行字符串联接。

是否有内置函数或类似函数可以在T-SQL中完成此任务?

===============>>#1 票数:2 已采纳

这是使用递归CTE的一种可能的解决方案:

此处说明使用的方法

TSQL设置测试数据(我正在使用表变量):

DECLARE @QuestionRevisions TABLE  ( 
id INT IDENTITY NOT NULL, 
question INT NOT NULL, 
postDate DATETIME NOT NULL, 
contents NTEXT NOT NULL, 
creatingUser INT NOT NULL, 
title NVARCHAR(200) NOT NULL)

DECLARE @Tags TABLE ( 
id INT IDENTITY NOT NULL, 
name NVARCHAR(45) NOT NULL
)

DECLARE @QuestionTags TABLE ( 
tag INT NOT NULL, 
question INT NOT NULL
)
INSERT INTO @QuestionRevisions 
(question,postDate,contents,creatingUser,title)
VALUES
(1,GETDATE(),'Contents 1',1,'TITLE 1')

INSERT INTO @QuestionRevisions 
(question,postDate,contents,creatingUser,title)
VALUES
(2,GETDATE(),'Contents 2',2,'TITLE 2')

INSERT INTO @Tags (name) VALUES ('Tag 1')
INSERT INTO @Tags (name) VALUES ('Tag 2')
INSERT INTO @Tags (name) VALUES ('Tag 3')
INSERT INTO @Tags (name) VALUES ('Tag 4')
INSERT INTO @Tags (name) VALUES ('Tag 5')
INSERT INTO @Tags (name) VALUES ('Tag 6')

INSERT INTO @QuestionTags (tag,question) VALUES (1,1)
INSERT INTO @QuestionTags (tag,question) VALUES (3,1)
INSERT INTO @QuestionTags (tag,question) VALUES (5,1)
INSERT INTO @QuestionTags (tag,question) VALUES (4,2)
INSERT INTO @QuestionTags (tag,question) VALUES (2,2)

这是动作部分:

;WITH CTE ( id, taglist, tagid, [length] ) 
      AS (  SELECT question, CAST( '' AS VARCHAR(8000) ), 0, 0
            FROM @QuestionRevisions qr
            GROUP BY question
            UNION ALL
            SELECT qr.id
                ,  CAST(taglist + CASE WHEN [length] = 0 THEN '' ELSE ', ' END + t.name AS VARCHAR(8000) )
                ,  t.id
                ,  [length] + 1
            FROM CTE c 
            INNER JOIN @QuestionRevisions qr ON c.id = qr.question
            INNER JOIN @QuestionTags qt ON qr.question=qt.question
            INNER JOIN @Tags t ON t.id=qt.tag
            WHERE t.id > c.tagid )
SELECT id, taglist 
FROM ( SELECT id, taglist, RANK() OVER ( PARTITION BY id ORDER BY length DESC )
         FROM CTE ) D ( id, taglist, rank )
WHERE rank = 1;

===============>>#2 票数:1

这就是我最终选择的解决方案。 我勾选了Mack的答案,因为它可以处理任意数量的标签,并且可以匹配我在问题中所要求的内容。 我最终还是这么做了,但是,仅仅是因为我了解了它在做什么,而我却不知道Mack的工作原理:)

WITH tagScans (qRevId, tagName, tagRank)
AS (
    SELECT DISTINCT
        QuestionTags.question AS qRevId,
        Tags.name AS tagName,
        ROW_NUMBER() OVER (PARTITION BY QuestionTags.question ORDER BY Tags.name) AS tagRank
    FROM QuestionTags
    INNER JOIN Tags ON Tags.id = QuestionTags.tag
)
SELECT
    Questions.id AS id,
    Questions.currentScore AS currentScore,
    answerCounts.number AS answerCount,
    latestRevUser.id AS latestRevUserId,
    latestRevUser.caseId AS lastRevUserCaseId,
    latestRevUser.currentScore AS lastRevUserScore,
    CreatingUsers.userId AS creationUserId,
    CreatingUsers.caseId AS creationUserCaseId,
    CreatingUsers.userScore AS creationUserScore,
    t1.tagName AS tagOne,
    t2.tagName AS tagTwo,
    t3.tagName AS tagThree,
    t4.tagName AS tagFour,
    t5.tagName AS tagFive
FROM Questions
INNER JOIN QuestionRevisions ON QuestionRevisions.question = Questions.id
INNER JOIN
(
    SELECT
        Questions.id AS questionId,
        MAX(QuestionRevisions.id) AS maxRevisionId
    FROM Questions
    INNER JOIN QuestionRevisions ON QuestionRevisions.question = Questions.id
    GROUP BY Questions.id
) AS LatestQuestionRevisions ON QuestionRevisions.id = LatestQuestionRevisions.maxRevisionId
INNER JOIN Users AS latestRevUser ON latestRevUser.id = QuestionRevisions.creatingUser
INNER JOIN
(
    SELECT
        QuestionRevisions.question AS questionId,
        Users.id AS userId,
        Users.caseId AS caseId,
        Users.currentScore AS userScore
    FROM Users
    INNER JOIN QuestionRevisions ON QuestionRevisions.creatingUser = Users.id
    INNER JOIN
    (
        SELECT
            MIN(QuestionRevisions.id) AS minQuestionRevisionId
        FROM Questions
        INNER JOIN QuestionRevisions ON QuestionRevisions.question = Questions.id
        GROUP BY Questions.id
    ) AS QuestionGroups ON QuestionGroups.minQuestionRevisionId = QuestionRevisions.id
) AS CreatingUsers ON CreatingUsers.questionId = Questions.id
INNER JOIN
(
    SELECT
        COUNT(*) AS number,
        Questions.id AS questionId
    FROM Questions
    INNER JOIN Answers ON Answers.question = Questions.id
    GROUP BY Questions.id
) AS answerCounts ON answerCounts.questionId = Questions.id
LEFT JOIN tagScans AS t1 ON t1.qRevId = QuestionRevisions.id AND t1.tagRank = 1
LEFT JOIN tagScans AS t2 ON t2.qRevId = QuestionRevisions.id AND t2.tagRank = 2
LEFT JOIN tagScans AS t3 ON t3.qRevId = QuestionRevisions.id AND t3.tagRank = 3
LEFT JOIN tagScans AS t4 ON t4.qRevId = QuestionRevisions.id AND t4.tagRank = 4
LEFT JOIN tagScans AS t5 ON t5.qRevId = QuestionRevisions.id AND t5.tagRank = 5
ORDER BY QuestionRevisions.postDate DESC

===============>>#3 票数:1

这是一个常见的问题,经常以多种不同的方式来表达(将行连接为字符串,将行合并为字符串,将行压缩为字符串,将行合并为字符串等)。 在SQL Server中,有两种公认的方法可以将任意数量的行合并为单个字符串。

第一种方法(通常是最简单的方法)是将XML PathSTUFF函数结合使用,如下所示:

select rsQuestions.QuestionID,
       stuff((select ', '+ rsTags.TagName
              from @Tags rsTags  
              inner join @QuestionTags rsMap on rsMap.TagID = rsTags.TagID
              where rsMap.QuestionID = rsQuestions.QuestionID
              for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '')
from @QuestionRevisions rsQuestions 

这是一个有效的示例 (从Mack借用一些稍作修改的设置)。 为了您的目的,您可以将该查询的结果存储在公用表表达式或子查询中(我将保留其作为练习)。

第二种方法是使用递归公用表表达式 这是一个有注释的示例,该示例将如何工作:

--NumberedTags establishes a ranked list of tags for each question.
--The key here is using row_number() or rank() partitioned by the particular question
;with NumberedTags (QuestionID, TagString, TagNum) as
(
    select  QuestionID,
            cast(TagName as nvarchar(max)) as TagString,
            row_number() over (partition by QuestionID order by rsTags.TagID) as TagNum
    from @QuestionTags rsMap
    inner join @Tags rsTags on rsTags.TagID = rsMap.TagID
),
--TagsAsString is the recursive query
TagsAsString (QuestionID, TagString, TagNum) as
(
    --The first query in the common table expression establishes the anchor for the 
    --recursive query, in this case selecting the first tag for each question
    select  QuestionID,
            TagString,
            TagNum
    from NumberedTags 
    where TagNum = 1

    union all

    --The second query in the union performs the recursion by joining the 
    --anchor to the next tag, and so on...
    select  NumberedTags.QuestionID,
            TagsAsString.TagString + ', ' + NumberedTags.TagString,
            NumberedTags.TagNum
    from    NumberedTags
    inner join TagsAsString on TagsAsString.QuestionID = NumberedTags.QuestionID
                           and NumberedTags.TagNum = TagsAsString.TagNum + 1
)
--The result of the recursive query is a list of tag strings building up to the final
--string, of which we only want the last, so here we select the longest one which
--gives us the final result
select QuestionID, max(TagString) 
from TagsAsString                         
group by QuestionID

这是一个工作版本 同样,您可以在公用表表达式或子查询中使用结果与其他表联接以获得最终结果。 希望注释可以帮助您更多地了解递归公用表表达式的工作原理(尽管Macks答案中的链接也对该方法进行了详细介绍)。

当然,还有另一种方法,它不能处理任意数量的行,而是要多次对别名的表进行连接,这就是您在回答中所做的。

  ask by Billy ONeal translate from so

未解决问题?本站智能推荐:

2回复

如何将多个字符串绑定到游标中的1个变量中?

我想将一个变量的字符串绑定到另一个变量,为每个游标重复。 我目前有这个: 现在,我希望光标中@varA的所有记录(@varA在字符串中)都串联到@varB中,例如:@varB ='Acbd,efgh,xyz ...' 我该怎么做?
7回复

在SQL查询中替换多个字符串

我正在SQL Server中编写一个SQL查询,我需要用一个字符串值替换多个字符串值。 例如 会成为 我知道如何执行此操作的唯一方法是在SELECT子句中使用嵌套的REPLACE。 有没有更简单的方法? 编辑:产品类别中可能还有其他值。 见上面编辑的例子。
3回复

如何将字段中的每个字符替换为单个字符(但具有相同的文本长度)

我正在尝试将值“ something nice”的列更新为“ X”,但结果必须包含与原始字符完全相同的字符数。 空格必须忽略。 我尝试使用PATINDEX但没有成功。 我尝试用patindex替换,然后用左,右替换其他示例,这些示例与我的不太相似,但是没有成功。 一些原始输出:
3回复

SQL逗号分隔列表作为单个字符串

在我们的业务中,我们有一个主要基本帐户,然后在该基本帐户下有一个附属帐户。 1.)如何将所有帐户(包括基本帐户(以逗号分隔))放入一个列中? 我以前在其他数据集上使用过此代码,并且效果很好。 我只是想不通如何使用所有多个联接来实现此目的。 我的输出: 我希望看到这
5回复

检查字符串中每个字符是否与SQL中的另一个字符串一起存在

我有一串 我还有另一个弦 我需要比较两个字符串,如果@Str1中的每个字符都在@Str2存在,则返回1 即使@Str2为'45132',它也必须返回1, 如果@Str2为456 ,则必须返回0 即使@ Str2为'45132',它也必须返回1 我不建议使用
4回复

我如何将一行中的所有值连接成一个字符串?

假设我有一行数据,存储如下: 我怎样才能将它整合成一个字符串,如下所示? 此表中的列标题(和列标题的数量)将不知道,因此按列名选择不是一个选项(?)。 请注意,我不是试图在列中合并值列表,我试图在一行中合并值存储。 我也更愿意避免使用枢轴,因为我将处理大量数据并且不希望将
3回复

我如何从SQL中的列中获取某个字符串

列名主题 价值=东西TEST001东西 我需要从“某事TEST001某物”中获取TEST001 如果TEST001的1后面有空格或任何特殊字符,它们将被删除。 我只有这个 TEST001的数字可以更多,但如果有空格或非数字,它将被删除。
1回复

将表列合并为单个字符串(不含UDF)

我正在尝试将多个记录中的字段组合成 SQL Server或T-SQL中的单个字符串 。 例如 ,我有: ...... 我需要 : This is a test! 我可以使用LAG 组合两个记录 (见下文),我怀疑解决方案中有一个WHILE (可能是这个RedGate
2回复

在T-SQL中,用一个字符替换字符串中的字母,用另一个字符替换数字

我有类似“ A9Tf6Uv54 ”的字符串,现在我想将其替换为“ xzxxzxxzz ”格式。 换句话说,字母字符被替换为“ x ”,数字被替换为“ z ”。 如何在SQL中用不同的字符替换数字和字母?
4回复

MSSQL将某个字符后的字符串替换为另一个字符串

我在sql过程中收到一个电子邮件ID。我需要用定义的字符串替换电子邮件客户端。 假设我收到了电子邮件ID,例如abc@gmail.com或pqr@yahoo.com或mnz@hotmail.com,在这种情况下,我需要用固定的字符串替换@ gmail.com / @ yahoo.com。