嵌套单个查询,用于连接单列中多行中的特定列

时间:2013-08-24 17:13:52

标签: sql sql-server sql-server-2008

我目前正在使用sql server并编写存储过程来检索一些数据。 我的要求如下:

Table A:PersonId,FirstName,LastName,Address,CourseId
(Primary Key For Table B,Foreign Key Here)
Table B:CourseDescription,CourseId

现在,每门课程都可能有多名学生注册该课程。我的要求是连接每个学生 LastName FirstName 。如果课程中有3个或更多学生,我们必须在结果中将另一个标志值设置为“Y”。我已经使用临时表完成了存储过程,并逐步更新它。 我的存储过程也是这样的: 临时表有列:

SeqId,CourseId,CourseDescription,StudentNameConcat,IsMoreThan3

首先我更新课程ID,说明。然后从这个表中我循环基于序列id(SeqId)并检索学生名称列表作为列值并在声明的变量中连接它。

这种方法并不好,因为它不是基于集合的方法,我相信在使用内部查询或循环连接的单个查询中必须有替代方法。我仍在阅读并尝试在单一查询。但仍然没有得到任何线索。

4 个答案:

答案 0 :(得分:2)

您的数据结构未规范化。除非学生只能参加一门课程。这很糟糕,应予以纠正。

除此之外,您正在尝试模拟group concat函数。这是一种方法。

SELECT courses.*, 
    LEFT(students , LEN(students)-1) AS students, 
    case when (select count(*) from @studentcourses where CourseId = courses.CourseId)>=3
      then 'y' else 'n' end
FROM courses
CROSS APPLY
(
    SELECT lastname + ' ' + firstname + ','
    FROM studentcourses
    WHERE courses.CourseId = studentcourses.CourseId
    FOR XML PATH('')
) t (students)

答案 1 :(得分:2)

select
    c.CourseId,
    c.CourseDescription,
    stuff(
        (
            select ', ' + s.FirstName + ' ' + s.LastName
            from Students as s
            where s.CourseId = c.CourseId
            for xml path(''), type
        ).value('.', 'nvarchar(max)')
    , 1, 2, '') as Students,
    case
        when (select count(*) from Students as s where s.CourseId = c.CourseId) >=3 then 'y'
        else 'n'
    end as flag
from Courses as c

简要说明。首先,将字符串连接到xml,但name为空,元素数据为', ' + s.FirstName + ' ' + s.LastName。之后,您使用value方法将此xml作为nvarchar(max)(这将保留名称中的所有特殊字符,如&>)。在那之后,你将连接你的名字,但之前你会有一个逗号,所以你需要使用stuff函数来剪切它。

sql fiddle demo

答案 2 :(得分:2)

您需要另一张表来跟踪每门课程注册的学生:

CREATE TABLE dbo.AsocCoursePerson(
    AsocCoursePerson INT PRIMARY KEY,
    CourseID INT NOT NULL REFERENCES dbo.Course(CourseID),
    PersonID INT NOT NULL REFERENCES dbo.Person(PersonID)
);

解决方案(SQL Fiddle demo):

SELECT  c.CourseID,
        c.[Description] AS CourseDescription,
        STUFF(oa.XmlCol.value('(/result/@PersonsFullName)[1]','NVARCHAR(MAX)'),1,1,'') AS StudentNameConcat,
        ISNULL(oa.XmlCol.value('(/result/@PersonsCnt)[1]','INT'),0) AS StudentsCount,
        CASE WHEN ISNULL(oa.XmlCol.value('(/result/@PersonsCnt)[1]','INT'),0) >= 3 THEN 'Y' ELSE 'N' END AS IsMoreThan3
FROM    dbo.Course c
OUTER APPLY(
    SELECT 
    (
        SELECT  p.FirstName + ' ' + p.LastName AS PersonFullName
        FROM    dbo.AsocCoursePerson asoc
        JOIN    dbo.Person p ON asoc.PersonID = p.PersonID
        WHERE   asoc.CourseID = c.CourseID
        FOR XML RAW('row'), TYPE
    ).query('
        <result 
            PersonsFullName="{for $p in /row return concat(",",$p/@PersonFullName)}" 
            PersonsCnt="{count(/row)}"
        />
    ') XmlCol
) oa;

答案 3 :(得分:1)

如果你想要做的只是标记每个人的课程,如果课程中至少有3人,请尝试这样的事情:

SELECT T1.COURSEID, 
       T2.COURSEDESCRIPTION, 
       FIRSTNAME + ' ' + LASTNAME StudentNameConcat, 
       CASE 
         WHEN COUNT(*) 
                OVER ( 
                  PARTITION BY t1.COURSEID) >= 3 THEN 1 
         ELSE 0 
       END                        IsMoreThan3 
FROM   TABLEA T1 
       INNER JOIN TABLEB T2 
               ON T1.COURSEID = T2.COURSEID 

您可以查看SQL Fiddle上的工作示例 如果你想解释它是如何工作的,请给我留言。