SQL Server 2000: идеи для выполнения подзапроса агрегации агрегации - PullRequest
7 голосов
/ 09 марта 2010

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

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

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

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


Faking It

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

Ответы [ 4 ]

3 голосов
/ 09 марта 2010

Посмотрите на эти статьи:

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

http://www.simple -talk.com / sql / t-sql-программирование / сцепление-значения-строки-в-транзакции-sql / (См. Решение по перекрестному объединению Фила Фактора в ответах - которое будет работа в SQL Server 2000)

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

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

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

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

1 голос
/ 10 ноября 2011

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

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

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

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

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

0 голосов
/ 09 марта 2010

Вы также можете взглянуть на этот скрипт .Это в основном метод перекрестного соединения, который Cade Roux также упомянул в своем посте.

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

0 голосов
/ 09 марта 2010

Я не уверен, что это работает в 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

ВЫХОД, аналогично исходному запросу

...