Объединить значения столбцов из строк

Я использую систему расписания с серверной частью 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 является уникальным событием, это будет работать только тогда, когда я знаю точный идентификатор, я не могу запустить его для каждого идентификатора.

Есть ли способ сделать это для каждого отдельного event_id без курсоров. Я видел возможное решение этого в использовании UDF, к сожалению, моя вторая проблема заключается в том, что система расписания (CELCAT) создает новую базу данных для каждого года (я знаю, не спрашивайте), поэтому мне придется сделать SQL динамическим, т.е. база данных следующего года будет celcat200910, я считаю, что динамический SQL не может быть запущен в UDF.

Помните, что это SQL Server 2000.


person PeteT    schedule 25.06.2009    source источник
comment
Об аранжировках БД: звучит грубо. Да, никаких динамических SQL в пользовательских функциях не допускается. В этом потоке вы можете найти полезную помощь для работы с несколькими БД: оператор с переменной"> stackoverflow.com/questions/1037174/ ... однако, я думаю, что лучшим решением было бы попытаться найти какой-то способ поместить данные в одну БД где-нибудь - но это может быть выше и выходит за рамки вашей компетенции / служебного долга.   -  person Joel Goodwin    schedule 25.06.2009
comment
Я помещаю большую часть данных в отдельную базу данных для данных посещаемости и тому подобного. Я использую ночную процедуру для заполнения этого материала, однако этот конкретный запрос должен быть активным.   -  person PeteT    schedule 26.06.2009


Ответы (5)


Вы все еще можете использовать представление, как предложил 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
person Derek Flenniken    schedule 30.06.2009

Для вашей второй проблемы используйте VIEW. Создайте представление для каждой интересующей таблицы в базе данных celcat и используйте вместо этого представления.

Когда база данных перейдет на следующий год, просто обновите все представления, чтобы они указывали на новую базу данных. Каждый запрос в системе, использующий VIEW, теперь будет обращаться к правильной базе данных.

person Joel Goodwin    schedule 25.06.2009
comment
Я знаю, что вы говорите, но обычно запросы фактически используют несколько лет базы данных. Я работаю в колледже, и в celcat, а также в расписании есть посещаемость студентов, это сравнивается по лотам. - person PeteT; 25.06.2009

Вы можете создать 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
person Andomar    schedule 25.06.2009
comment
возвращает varchar(4000) DECLARE @Tutors как varchar(8000) tsk. тск. также isnull(@Tutors + ' + ', '') --› @Tutors + ' + ' никогда не бывает нулевым - person Coentje; 29.06.2009
comment
Тип возвращаемого значения исправлен, но второе предложение неверно: @Tutors будет нулевым для первой найденной строки. - person Andomar; 29.06.2009

  • Могу я спросить, зачем вам нужно объединять имена на сервере? Разве клиентское приложение не могло сделать это за вас?

  • Если у вас возникли проблемы с адресацией таблиц в других базах данных, создайте представления со стандартными именами, по одному на таблицу, которые просто выбирают * из каждой таблицы. Вы можете написать 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(), чтобы охватить максимально возможное количество сотрудников на мероприятие.

Чтобы получить больше данных о событии, не помещайте его во временную таблицу (это замедлит работу). Просто используйте этот большой запрос как отдельную производную таблицу и присоединитесь к нужным таблицам.

person ErikE    schedule 02.07.2009

За последние несколько месяцев я немного разрабатывал celcat — это что-то вроде кошмара, и я вам сочувствую!

Честно говоря, в этой ситуации вам, вероятно, было бы лучше использовать API Celcat (к которому нужно немного привыкнуть, но он достаточно мощный и имеет то преимущество, что ваши запросы должны быть достаточно безопасными в разных версиях).

Я создал класс, который использовал для выбора конкретных версий базы данных и т. д., создав сеанс, специфичный для учебного года, который я хотел использовать.

В API также есть возможность напрямую запускать SQL, если вам это нужно.

Я знаю, что это не ответ на ваш вопрос, но я надеюсь, что это решит вашу проблему!

person Hooloovoo    schedule 13.07.2009