SQL-сервер 2000:Идеи для выполнения подзапроса агрегации конкатенации

StackOverflow https://stackoverflow.com/questions/2410474

Вопрос

у меня есть запрос, который возвращает нужные мне строки, например.

QuestionID  QuestionTitle  UpVotes  DownVotes  
==========  =============  =======  =========  
2142075     Win32: Cre...        0          0  
2232727     Win32: How...        2          0  
1870139     Wondows Ae...       12          0  

Теперь я хочу иметь столбец возвращается, который содержит разделенный запятыми список "Авторы"(напр.оригинальный постер и редакторы).например.:

QuestionID  QuestionTitle  UpVotes  DownVotes  Authors
==========  =============  =======  =========  ==========
2142075     Win32: Cre...        0          0  Ian Boyd  
2232727     Win32: How...        2          0  Ian Boyd, roygbiv
1870139     Wondows Ae...       12          0  Ian Boyd, Aaron Klotz, Jason Diller, danbystrom


Подделывая это

SQL Server 2000 не имеет CONCAT(AuthorName, ', ') операция агрегации, я ее притворялся — выполнял простые подвыборки для TOP 1 автор, и автор считается.

QuestionID  QuestionTitle  UpVotes  DownVotes  FirstAuthor  AuthorCount  
==========  =============  =======  =========  ===========  =========== 
2142075     Win32: Cre...        0          0  Ian Boyd               1 
2232727     Win32: How...        2          0  Ian Boyd               2
1870139     Wondows Ae...       12          0  Ian Boyd               3

Если авторов несколько, я показываю пользователю многоточие («…»), чтобы указать, что их несколько.напримерпользователь увидит:

QuestionID  QuestionTitle  UpVotes  DownVotes  Authors
==========  =============  =======  =========  ==========
2142075     Win32: Cre...        0          0  Ian Boyd
2232727     Win32: How...        2          0  Ian Boyd, …
1870139     Wondows Ae...       12          0  Ian Boyd, …

И это работает достаточно хорошо, поскольку обычно вопрос не редактируется - это означает, что я полностью поддерживаю случай 99%, а случай 1% - только половинузадница также.


Потоковый повторный запрос

В качестве более сложного и подверженного ошибкам решения я думал об итерации отображаемого списка и запуске рабочего потока пула потоков для каждого "вопрос" в списке выполните запрос к базе данных, чтобы получить список авторов, а затем агрегируйте список в памяти.Это будет означать, что список заполняется первым в (собственном) приложении.Затем я отправляю несколько тысяч отдельных запросов.

Но это будет ужасно, ужасно, ужасно медленно.Не говоря уже об ошибках, поскольку это будет потоковая работа.


Да, да, да

Адам Механик говорит совершенно прямо:

Не объединяйте строки в разграниченные строки в SQL Server.Сделайте это на стороне клиента.

Скажи мне как, и я это сделаю.


/плакать

Может ли кто-нибудь придумать лучшее решение, которое было бы столь же быстрым (скажем... на порядок), чем мое исходное решение «ТОП 1 плюс эллипсы»?

Например, есть ли способ вернуть набор результатов, если строка достижения имеет связанный набор результатов?Таким образом, для каждой «главной» строки я мог бы получить «подробный» набор результатов, содержащий список.


Код для лучшего ответа

Кейда ссылка к Решение Адама Маханика мне нравится лучшее.Пользовательская функция, которая, похоже, работает по волшебству:

CREATE FUNCTION dbo.ConcatAuthors(@QuestionID int)
RETURNS VARCHAR(8000)
AS
BEGIN
    DECLARE @Output VARCHAR(8000)
    SET @Output = ''

    SELECT @Output =    CASE @Output 
                WHEN '' THEN AuthorName 
                ELSE @Output + ', ' + AuthorName 
                END
    FROM  (
        SELECT QuestionID, AuthorName, QuestionDate AS AuthorDate FROM Questions
        UNION
        SELECT QuestionID, EditorName, EditDate FROM QuestionEdits
    ) dt
    WHERE dt.QuestionID = @QuestionID
    ORDER BY AuthorDate

    RETURN @Output
END

При использовании T-SQL:

SELECT QuestionID, QuestionTitle, UpVotes, DownVotes, dbo.ConcatAuthors(AuthorID)
FROM Questions
Это было полезно?

Решение

Взгляните на эти статьи:

http://dataeducation.com/rowset-string-concatenation-that-method-is-best/

http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/ (См. решение перекрестного соединения Фила Фактора в ответах, которое будет работать в SQL Server 2000)

Очевидно, что в SQL Server 2005 прием FOR XML является самым простым, гибким и, как правило, наиболее производительным.

Что касается возврата набора строк для каждой строки, если вы по какой-то причине все еще хотите это сделать, вы можете сделать это в хранимой процедуре, но клиенту нужно будет использовать все строки в первом наборе строк, а затем перейти к следующему. набор строк и свяжите его с первой строкой в ​​первом наборе строк и т. д.Вашему SP потребуется открыть курсор на том же наборе, который он вернул в качестве первого набора строк, и последовательно выполнить несколько операций выбора, чтобы сгенерировать все дочерние наборы строк.Я использовал эту технику, но только там, где ВСЕ данные действительно были необходимы (например, в полностью заполненном древовидном представлении).

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

Где бы вы это ни делали, это должно быть обоснованное решение на основе твой вариант использования.

Другие советы

Я попробовал 3 подхода к этому решению: тот, который опубликован здесь: сценарии ActiveX и функции UDF.

Самым эффективным сценарием (с точки зрения скорости) для меня был странный сценарий Axtive-X, выполняющий несколько запросов для получения дополнительных данных для объединения.

Преобразование UDF занимало в среднем 22 минуты, метод Subquery (опубликованный здесь) — около 5 минут, а сценарий activeX — 4 минуты 30 минут, к моему большому раздражению, поскольку именно от этого сценария я надеялся отказаться.Мне придется посмотреть, смогу ли я добиться еще большей эффективности в другом месте.

Я думаю, что дополнительные 30 секунд используются базой данных tempdb для хранения данных, поскольку мой сценарий требует порядка.

Следует отметить, что я объединяю огромные объемы текстовых данных.

Вы также можете взглянуть на это сценарий.По сути, это подход перекрестного соединения, о котором Кейд Ру также упомянул в своем посте.

Приведенный выше подход выглядит очень чисто:вам нужно сначала создать представление, а затем создать оператор на основе значений в представлении.Второй оператор sql вы можете динамически встроить в свой код, поэтому его легко использовать.

Я не уверен, работает ли это в SQL Server 2000, но вы можете попробовать:

--combine parent and child, children are CSV onto parent row
CREATE TABLE #TableA (RowID int, Value1 varchar(5), Value2 varchar(5))
INSERT INTO #TableA VALUES (1,'aaaaa','A')
INSERT INTO #TableA VALUES (2,'bbbbb','B')
INSERT INTO #TableA VALUES (3,'ccccc','C')

CREATE TABLE #TableB (RowID int, TypeOf varchar(10))
INSERT INTO #TableB VALUES (1,'wood')
INSERT INTO #TableB VALUES (2,'wood')
INSERT INTO #TableB VALUES (2,'steel')
INSERT INTO #TableB VALUES (2,'rock')
INSERT INTO #TableB VALUES (3,'plastic')
INSERT INTO #TableB VALUES (3,'paper')


SELECT
    a.*,dt.CombinedValue
    FROM #TableA        a
        LEFT OUTER JOIN (SELECT
                             c1.RowID
                                 ,STUFF(
                                          (SELECT
                                               ', ' + TypeOf
                                               FROM (SELECT
                                                         a.RowID,a.Value1,a.Value2,b.TypeOf
                                                         FROM #TableA                 a
                                                             LEFT OUTER JOIN #TableB  b ON a.RowID=b.RowID
                                                    ) c2
                                               WHERE c2.rowid=c1.rowid
                                               ORDER BY c1.RowID, TypeOf
                                               FOR XML PATH('') 
                                          )
                                          ,1,2, ''
                                       ) AS CombinedValue
                             FROM (SELECT
                                       a.RowID,a.Value1,a.Value2,b.TypeOf
                                       FROM #TableA                 a
                                           LEFT OUTER JOIN #TableB  b ON a.RowID=b.RowID
                                  ) c1
                             GROUP BY RowID
                        ) dt ON a.RowID=dt.RowID

ВЫВОД из SQL Server 2005:

RowID       Value1 Value2 CombinedValue
----------- ------ ------ ------------------
1           aaaaa  A      wood
2           bbbbb  B      rock, steel, wood
3           ccccc  C      paper, plastic

(3 row(s) affected)

РЕДАКТИРОВАТЬ запрос, который заменяет FOR XML PATH на FOR XML RAW, поэтому это должно работать на SQL Server 2000.

SELECT
    a.*,dt.CombinedValue
    FROM #TableA        a
        LEFT OUTER JOIN (SELECT
                             c1.RowID
                                 ,STUFF(REPLACE(REPLACE(
                                          (SELECT 
                                               ', ' + TypeOf as value
                                               FROM (SELECT
                                                         a.RowID,a.Value1,a.Value2,b.TypeOf
                                                         FROM #TableA                 a
                                                             LEFT OUTER JOIN #TableB  b ON a.RowID=b.RowID
                                                    ) c2
                                               WHERE c2.rowid=c1.rowid
                                               ORDER BY c1.RowID, TypeOf
                                               FOR XML RAW
                                          )
                                         ,'<row value="',''),'"/>','')
                                   , 1, 2, '') AS CombinedValue
                             FROM (SELECT
                                       a.RowID,a.Value1,a.Value2,b.TypeOf
                                       FROM #TableA                 a
                                           LEFT OUTER JOIN #TableB  b ON a.RowID=b.RowID
                                  ) c1
                             GROUP BY RowID
                        ) dt ON a.RowID=dt.RowID

ВЫХОД, такой же, как исходный запрос

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top