【问题标题】:Concatenate column values from rows连接行中的列值
【发布时间】:2010-11-05 19:09:52
【问题描述】:

我正在使用带有 SQL Server 2000 后端的时间表系统,我需要列出带有导师和房间的事件,这可能超过 1 个,因此可以将多行房间和导师变成 + 分隔列表。我过去使用过以下代码:

DECLARE @Tutors as varchar(8000)

SELECT @Tutors = isnull(@Tutors + ' + ', '') + name
FROM (
    SELECT CT_EVENT_STAFF.event_id, CT_EVENT_STAFF.weeks, 
        CT_STAFF.unique_name, CT_STAFF.name
    FROM celcat200809.dbo.CT_EVENT_STAFF AS CT_EVENT_STAFF 
    LEFT OUTER JOIN celcat200809.dbo.CT_STAFF AS CT_STAFF 
        ON CT_EVENT_STAFF.staff_id = CT_STAFF.staff_id
    WHERE event_id = @eventID
) As data_set

print @Tutors

event_id 是唯一的事件,这只有在我知道确切的 ID 时才有效,我不能为每个 ID 运行它。

有没有办法在没有游标的情况下为每个单独的 event_id 执行此操作。不幸的是,我在使用 UDF 时看到了一个可能的解决方案,我的第二个问题是时间表系统(CELCAT)每年都会创建一个新数据库(我知道不要问),所以我将不得不使 SQL 动态化,即明年的数据库将是celcat200910,我相信动态SQL不能在UDF中运行。

请记住这是 SQL Server 2000

【问题讨论】:

  • 关于数据库安排:听起来很粗糙。是的,UDF 中不允许使用动态 SQL。您可能会在此线程中找到一些有用的帮助,用于多数据库工作:stackoverflow.com/questions/1037174/… ... 但是,我认为最好的解决方案是尝试找到某种方法将数据放在单个数据库中的某个地方 - 但这可能高于和超出你的职权范围/职责范围。
  • 我将大部分数据放入单独的数据库中,用于出勤数据和类似数据。我使用一夜之间的过程来填充这些东西,但是这个特定的查询需要是实时的

标签: sql sql-server sql-server-2000


【解决方案1】:

您仍然可以按照 goodgai 的建议使用视图,但不要让它重定向到一个表,而是让它联合选择这些表。如果尚未完成并且您需要它,可以将年/月分成列。

CREATE VIEW UNIFIED_CT_STAFF
AS
SELECT year = 2008, month = 9, unique_name, name FROM celcat200809.dbo.CT_STAFF
UNION SELECT year = 2008, month = 10, unique_name, name FROM celcat200810.dbo.CT_STAFF

【讨论】:

    【解决方案2】:

    对于第二个问题,请使用 VIEW。为 celcat 数据库中每个感兴趣的表创建一个视图,然后使用这些视图。

    当数据库进展到下一年时,只需更新所有视图以指向新数据库。现在,系统中使用 VIEW 的每个查询都将寻址到正确的数据库。

    【讨论】:

    • 我知道你在说什么,但通常查询实际上使用了多个数据库年。我在一所大学工作,celcat 以及时间表都有学生出勤率,这是在很多地方进行比较的。
    【解决方案3】:

    您可以创建一个 UDF 来计算字符串,然后像这样使用它:

    select event_id, dbo.GetTutorsText(@eventId)
    from EventsTable
    

    UDF 可以这样定义:

    if object_id('dbo.GetTutorText') is not null 
        drop function dbo.GetTutorText
    go
    create function dbo.GetTutorText(
        @eventID int)
    returns varchar(8000)
    as
    begin
    DECLARE @Tutors as varchar(8000)
    
    SELECT @Tutors = isnull(@Tutors + ' + ', '') + name
    FROM (
        SELECT CT_EVENT_STAFF.event_id, CT_EVENT_STAFF.weeks, 
            CT_STAFF.unique_name, CT_STAFF.name
        FROM celcat200809.dbo.CT_EVENT_STAFF AS CT_EVENT_STAFF 
        LEFT OUTER JOIN celcat200809.dbo.CT_STAFF AS CT_STAFF 
            ON CT_EVENT_STAFF.staff_id = CT_STAFF.staff_id
        WHERE event_id = @eventID
    ) As data_set
    
    return @Tutors
    end
    go
    

    【讨论】:

    • 返回 varchar(4000) DECLARE @Tutors 作为 varchar(8000) tsk。啧啧。也 isnull(@Tutors + ' + ', '') --> @Tutors + ' + ' 永远不会为空
    • 更正了返回类型,但第二个建议是错误的:@Tutors 将在找到的第一行为空。
    【解决方案4】:
    • 请问为什么需要连接服务器上的名称?客户端应用程序不能为您执行此操作吗?

    • 如果您在处理其他数据库中的表时遇到问题,请创建具有标准化名称的视图,每个表一个,只需从每个表中选择 *。您可以编写一个自动创建视图的 SP,让您只传入要将所有视图设置到的数据库的名称。视图不会对性能造成任何重大影响。

    • 由于您使用的是 CT_STAFF 的左连接,这让我相信员工可能会丢失,在这种情况下,您将丢失连接它们的表达式的数据,因为它不允许 NULL员工姓名(每次遇到 NULL 员工姓名时都会重置列表)。

    这里有一个查询可以满足你的需要,虽然它有点小技巧:

    SELECT
       seqid = identity(int, 1, 1),
       event_id,
       S.name
    INTO #EventNames
    FROM
       celcat200809.dbo.CT_EVENT_STAFF ES
       LEFT JOIN celcat200809.dbo.CT_STAFF S ON ES.staff_id = S.staff_id
    ORDER BY
       event_id,
       S.name --optional, whatever you like here.
    
    SELECT
       EN.event_id,
       Max(CASE seqid - minseqid WHEN 0 THEN EN.name ELSE '' END))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 1 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 2 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 3 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 4 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 5 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 6 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 7 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 8 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 9 THEN EN.name ELSE NULL END, ''))
       + Max(Coalesce(' + ' + CASE seqid - minseqid WHEN 10 THEN EN.name ELSE NULL END, ''))
    FROM
       #EventNames EN
       INNER JOIN (
          SELECT event_id, minseqid = Min(seqid) FROM #EventNames GROUP BY event_id
       ) X ON EN.event_id = X.event_id
    GROUP BY EN.event_id
    

    只需确保放入足够多的 Max() 表达式,以涵盖每个活动中尽可能多的员工。

    要获取有关事件的更多数据,请不要将其放在临时表中(这会使其变慢)。只需将这个大查询用作它自己的派生表并连接回您需要的表。

    【讨论】:

      【解决方案5】:

      在过去的几个月里,我做了一些 celcat 开发——这有点像一场噩梦,我很同情你!

      说实话,在这种情况下,您可能会更好地使用 Celcat API(这需要一些时间来适应,但功能非常强大,并且具有跨版本查询应该相当安全的优势。)

      我创建了一个类,用于选择特定的数据库版本等。创建一个特定于我想使用的学年的会话。

      在 API 中,如果需要,还可以选择直接运行 SQL。

      我知道这不能回答你的问题,但我希望它能解决你的问题!

      【讨论】:

        猜你喜欢
        • 2013-10-27
        • 2021-11-17
        • 2021-06-15
        • 2022-08-14
        • 2022-11-22
        • 2012-01-12
        • 2013-07-17
        • 2018-12-04
        • 2021-06-16
        相关资源
        最近更新 更多