【问题标题】:Join multiple values from one column, selected from another table连接一列中的多个值,从另一个表中选择
【发布时间】:2015-05-03 21:44:27
【问题描述】:

鉴于这些简化的多项选择表,有时不止一个答案是正确的:

STUDENT_ANSWERS
AnswerID | StudentID | QuestionID | Answers
-------------------------------------------
       1 |         1 |          1 | C,D

QUESTION_ANSWERS
QuestionID | Answer | Text
-------------------------------------------------
         1 |      A | This is answer A
         1 |      B | B could also be correct
         1 |      C | Maybe it's C?
         1 |      D | Definitely D!

如何进行选择,将答案翻译成他们的描述?

我的开始:

SELECT *
FROM STUDENT_ANSWERS sa
LEFT OUTER JOIN QUESTION_ANSWERS qa ON qa.Answer IN sa.Answers???
 -- Doesn't seem to work as IN requires a format of ('C','D') while I have 'C,D'

期望的输出:

AnswerID | StudentID | QuestionID | AnswerDescriptions
-------------------------------------------
       1 |         1 |          1 | Maybe it's C?,Definitely D!

因此,描述只需替换代码,而不是为每个答案获取一行。

【问题讨论】:

    标签: sql sql-server tsql csv join


    【解决方案1】:

    您的问题是表STUDENT_ANSWERS 的结构。每个答案应该有一行:

    答案ID |学生证 |问题ID |回答 ------------------------------------------ 1 | 1 | 1 | C 2 | 1 | 1 | D

    现在,假设你不能做任何改变(阅读:修复)这个,你可以通过附加逗号和使用 LIKE 来伪造它:

    select *
    from STUDENT_ANSWERS a
    join QUESTION_ANSWERS q on ',' + a.Answers + ',' like '%,' + q.Answer + ',%'
        and a.QuestionID = q.QuestionID
    

    SQL Fiddle demo

    注意这一点假设您永远不会在QUESTION_ANSWERS.Answer 中拥有文本,。它也永远无法使用索引,所以它会比慢更慢。

    如果您绝对必须在数据库中将其格式化为一行,您可以使用STUFFFOR XML PATH('') 技巧来连接结果行。

    【讨论】:

    • 这很接近,但我的真实案例的代码看起来相似,而不是答案,比如 A 和 AA,如果答案是 A 而不是 AA,它会同时选择它们。
    • 真的吗?我很确定它可以工作 sqlfiddle.com/#!6/a564f/1,这就是我们必须经历连接所有这些逗号的麻烦的原因。
    【解决方案2】:

    这是仅使用 T-SQL 语句的完整工作示例。我会建议您创建单独的函数来拆分返回行集的CSV。此外,如果您正在处理大量数据,您可能需要创建一个 CLR 函数来拆分值。看看this article(这里有你需要的一切)。

    DECLARE @StudentAnswers TABLE
    (
         [AnswerID] INT
        ,[StudentID] INT
        ,[QuestionID] INT
        ,[Answers] VARCHAR(256)
    );
    
    DECLARE @QuestionAnswers TABLE
    (
         [QuestionID] INT
        ,[Answer] CHAR
        ,[Text] VARCHAR(256)
    );
    
    INSERT INTO @StudentAnswers ([AnswerID], [StudentID], [QuestionID], [Answers])
    VALUES (1, 1, 1, 'C,D')
          ,(2, 2, 1, 'A');
    
     INSERT INTO @QuestionAnswers ([QuestionID], [Answer], [Text])
     VALUES  (1, 'A', 'This is answer A')
            ,(1, 'B', 'B could also be correct')
            ,(1, 'C', 'Maybe it''s C?')
            ,(1, 'D', 'Definitely D!');
    
    SELECT SA.[AnswerID]
          ,SA.[StudentID]
          ,SA.[QuestionID]
          ,T.c.value('.', 'CHAR')
          ,QA.[Text]
    FROM @StudentAnswers SA
    CROSS APPLY 
    (
        SELECT CAST('<i>' + REPLACE([Answers], ',', '</i><i>') + '</i>' AS XML) Answers
    ) DS
    CROSS APPLY DS.Answers.nodes('i') T(c)
    INNER JOIN @QuestionAnswers QA
        ON SA.[QuestionID] = QA.[QuestionID]
        AND T.c.value('.', 'CHAR') = QA.[Answer];
    

    【讨论】:

    • 可爱地使用 XML 和 SQL Server 的选择能力。不确定它的性能是否比我的解决方案好,但它更具可读性。特别是如果您使用比&lt;i&gt; 更具描述性的标签并将T(c) 重构为函数/视图。
    • 确实,最好将拆分功能封装在单独的函数中。在我的实践中,使用CLR 函数进行拆分始终是最快的方法(此外,您可以根据返回的内容编写单独的函数 - 一个字符或多个字符)。我不确定JOINLIKE 子句在使用大表和CSV 字符串时的效果如何。
    【解决方案3】:

    试试这个

    select answerid,studentid,a.QuestionID,group_concat(b.text) from student_answers a left join QUESTION_ANSWERS b on b.questionid= a.questionid and  FIND_IN_SET(b.Answer, a.Answers)
    group by a.questionid
    

    确实有效。

    【讨论】:

    • 问题带有TSQL标签,这意味着它与Microsoft SQL Server相关,而不是mySQL
    猜你喜欢
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    • 2020-03-21
    • 1970-01-01
    • 2012-06-28
    • 1970-01-01
    • 2019-03-09
    • 1970-01-01
    相关资源
    最近更新 更多