【问题标题】:T-sql putting values of column into one rowT-sql将列的值放入一行
【发布时间】:2022-05-05 20:24:19
【问题描述】:

假设我有下表

ClassID    StudentID
1          10
1          11
2          12
2          10
3          13

我想要这样的桌子

ClassID  StudentID1   StudenID2 ...  StudentID32
1        10           11             null
2        12           10             null
3        3            null           null

有没有办法查询上表。约束:每个班级最多可以有 32 名学生(可以是任何预定义的人数)。我想while循环可以用某种方式,但应该还有其他方法。

编辑 我尝试使用 Pivot,但我必须使用某种聚合,但对于上述问题,我不必使用任何类型的聚合。在枢轴中,我必须为该列命名(类似于in(col1, col2,...)),但在这里我什至不知道学生的身份。是否有任何通用方法来解决上述简单问题,以便每一行代表所有学生的唯一 ClassID,其中每个 StudentID 在不同的列中(列可以命名为您喜欢的任何名称),每个 ClassID 最多可以有 32 个 StudentID(我不应该使用自连接,因为它不实用)

【问题讨论】:

  • 嗨 Sher,在我看来,如果您要使用 PIVOT 来实现您想要的结果,您的源代码中缺少一列。结果表中的列由StudentID决定,这些列中的值来自哪里?
  • 嗨,@KamranFarzami,正是因为这个原因,我不确定是否应该使用数据透视表
  • 顺便说一下,@KamranFarzami,列不应该完全是 StudentID1 等等。
  • 如果 10 是最大值,则使用 ROW_NUMBER() 对列进行透视,并使用 MIN(StudentID) 作为值。如果您不知道会有多少,那么您将需要构建一个动态 SQL 字符串。

标签: sql sql-server tsql


【解决方案1】:

我希望我能清楚地理解你。虽然我仍然使用 PIVOT,但在这里,我假设我也不知道学生证。

    create table Student (
    ClassID INT
    , StudentID INT
)

INSERT INTO Student (ClassID, StudentID) VALUES
 (1,10)
, (1,11)
, (2,12)
, (2,10)
, (3,13)

select
    'Student' + CONVERT(NVARCHAR(150),ROW_NUMBER () OVER (PARTITION BY ClassID ORDER BY StudentID)) AS StudentNo
    , *
into #tmpStud
from Student

declare @distinct nvarchar(max) = ''

/*
option a : flexible to the number of students
*/
--set @distinct = (select distinct '[' + StudentNo + '],' as [text()] from #tmpStud for xml path(''))
--set @distinct = SUBSTRING(@distinct, 0, LEN(@distinct))

/*
option b : max of 32 student
*/
declare @max int = 33
        , @loop int = 1

while (@loop < @max)
    begin
        if(@loop = 1) begin
            set @distinct = @distinct + '[Student' + Convert(nvarchar(20),@loop) + ']'
            set @loop = @loop + 1
        end
        else begin
            set @distinct = @distinct + ',[Student' + Convert(nvarchar(20),@loop) + ']'
            set @loop = @loop + 1
        end
    end

exec ('
select
    *
from (
    select
        ClassID
        , StudentNo
        , StudentID
    FROM #tmpStud
) AS s PIVOT
(
    MAX(StudentID)
    FOR StudentNo IN (' + @distinct + ')
) AS pvt
')

drop table #tmpStud

编辑:一旦您创建了学生表,请运行以下代码:

select
    'Student' + CONVERT(NVARCHAR(150),ROW_NUMBER () OVER (PARTITION BY ClassID ORDER BY StudentID)) AS StudentNo
    , *
into #tmpStud
from Student

declare @distinct nvarchar(max) = ''

/*
option a : flexible to the number of students
*/
set @distinct = (select distinct '[' + StudentNo + '],' as [text()] from #tmpStud for xml path(''))
set @distinct = SUBSTRING(@distinct, 0, LEN(@distinct))

exec ('
select
    *
from (
    select
        ClassID
        , StudentNo
        , StudentID
    FROM #tmpStud
) AS s PIVOT
(
    MAX(StudentID)
    FOR StudentNo IN (' + @distinct + ')
) AS pvt
')

drop table #tmpStud

编辑:假设您需要一个静态的 32 名学生 @ max..使用下面的脚本。

select
    'Student' + CONVERT(NVARCHAR(150),ROW_NUMBER () OVER (PARTITION BY ClassID ORDER BY StudentID)) AS StudentNo
    , *
into #tmpStud
from Student

declare @distinct nvarchar(max) = ''

/*
option b : static max of 32 student
*/
declare @max int = 33
        , @loop int = 1

while (@loop < @max)
    begin
        if(@loop = 1) begin
            set @distinct = @distinct + '[Student' + Convert(nvarchar(20),@loop) + ']'
            set @loop = @loop + 1
        end
        else begin
            set @distinct = @distinct + ',[Student' + Convert(nvarchar(20),@loop) + ']'
            set @loop = @loop + 1
        end
    end

exec ('
select
    *
from (
    select
        ClassID
        , StudentNo
        , StudentID
    FROM #tmpStud
) AS s PIVOT
(
    MAX(StudentID)
    FOR StudentNo IN (' + @distinct + ')
) AS pvt
')

drop table #tmpStud

【讨论】:

  • 看来应该可以了。但是查询返回错误。 distinct 没有声明,我已经修复了它。现在错误是')'​​附近的语法不正确。我无法修复这个错误。您能否编辑您的答案。谢谢
  • 是的,我很抱歉,@distinct 数据类型应该是 nvarchar(max)
  • 是的,我做到了,但无论如何它都会返回我之前评论中描述的错误。能否请您查看查询
  • 如果您需要“静态”32 名学生,我实际上有一个选项 B,如果您需要,我将粘贴下一个查询..稍等
  • 完成,请查看最新编辑,希望对您有所帮助。
【解决方案2】:

您需要结合使用 Count()、GROUP BY、临时表和 PIVOT 函数。

对于上面的示例,我有以下内容,它似乎有效。您可以修改 Pivot 以动态派生列名,但这需要花点心思。

WITH CTE
     AS (SELECT ClassID,
                COUNT(StudentID) AS StudentNum
         FROM KamTest.dbo.Table1
         GROUP BY ClassID)
     SELECT A.ClassID,
            B.StudentID,
            A.StudentNum INTO #temp
     FROM CTE AS A
     INNER JOIN
     KamTest.dbo.Table1 AS B
     ON A.ClassID=B.ClassID;

SELECT ClassID,
       MAX(StudentID),
       COUNT(StudentID)
FROM KamTest.dbo.Table1
GROUP BY ClassID;

SELECT ClassID,
       [10] AS StudentID10,
       [11] AS StudentID11,
       [12] AS StudentID12,
       [13] AS StudentID13
FROM(
    SELECT ClassID,
           StudentID,
           StudentNum
    FROM #temp) AS SourceTable PIVOT(MAX(StudentNum) FOR StudentID IN([10],
                                                                      [11],
                                                                      [12],
                                                                      [13])) AS PivotTable;

【讨论】:

  • 谢谢,但它并没有在不同的列中列出每个班级的所有学生 ID
  • 在列标题中。您可以使用它并对其进行修改以满足您的需求
【解决方案3】:

对于一个相对较小的已知限制(例如 10),自联接可以工作。

【讨论】:

  • 一会儿,我正在修复答案。
  • 谢谢,但这不是解决方案。限制可以是任何预定义的数字。我写了 10 只是为了解释我的问题
  • 我已经从答案中删除了代码,因为当没有像 10 这样小的固定限制时,自我加入是不可行的。
  • 我会考虑在客户端这样做。尽管 SQL Server 有一个数据透视工具(至少从 2008 年开始),但我认为这有点滥用服务器的角色,即维护和交付数据,而不是向用户呈现数据。如您所描述的旋转数据实际上是一种数据表示操作。表格和查询结果集不是电子表格,尽管它们看起来很简单。
猜你喜欢
  • 1970-01-01
  • 2021-10-25
  • 2012-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-15
  • 1970-01-01
相关资源
最近更新 更多