SQL Server 2000:Ideias para a realização de concatenação de agregação de subconsulta
-
18-09-2019 - |
Pergunta
eu tenho uma consulta que retorna linhas que eu quero, por exemplo,
QuestionID QuestionTitle UpVotes DownVotes
========== ============= ======= =========
2142075 Win32: Cre... 0 0
2232727 Win32: How... 2 0
1870139 Wondows Ae... 12 0
Agora eu quero ter um coluna voltou, que contém uma lista separada por vírgulas "Autores"(e.g.poster original e editores).por exemplo:
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
Fingir
SQL Server 2000 não tem um CONCAT(AuthorName, ', ')
agregação de operação, eu tenho de fingir - efectuar uma simples sub-seleciona para o TOP 1
autor, e o autor de contagem.
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
Se houver mais de um autor, então eu mostrar ao usuário uma reticências ("..."), para indicar que existe mais do que um.exemplo:o usuário poderia ver:
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, …
E que funciona bem o suficiente, pois, normalmente, um pergunta não é editado, o que significa que eu estou apoiando a 99% caso perfeitamente, e de 1% no caso de apenas a metade-assed bem.
Rosca Re-consulta
Como ser mais complicado, e bug propensas a solução, eu estava pensando em iterar a lista apresentada, e girando até que um thread pool de thread de trabalho para cada um "pergunta"na lista, execute uma consulta ao banco de dados para obter a lista de autores, agregando a lista na memória.Isto significa que a lista preenche o primeiro na (nativo) aplicações.Em seguida, eu o problema de alguns milhares de consultas individuais depois.
Mas o que seria terrivelmente, terrivelmente, terrivelmente lento.Para não mencionar o erro crivado, pois vai ser o thread de trabalho.
Yeah yeah yeah
Adam Mecânico diz muito claramente:
Não concatenar linhas delimitado cadeias de caracteres no SQL Server.Fazê-lo de cliente o lado.
Diga-me como e que eu vou fazer.
/cry
Alguém pode pensar em uma solução melhor, que é mais rápido (dizer...dentro de uma ordem de magnitude) do que o meu original "TOP 1 mais reticências" solução?
Por exemplo, existe uma maneira de retornar um conjunto de resultados, onde a chegar a linha tem associado um conjunto de resultados?Assim, para cada "mestre" linha", eu poderia chegar a um "detalhe" conjunto de resultados que contém a lista.
Código para melhor resposta
Cade link para Adam Machanic da solução eu como a melhor.Uma função definida pelo usuário, que parece operar através de magia:
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
Com uma T-SQL uso de:
SELECT QuestionID, QuestionTitle, UpVotes, DownVotes, dbo.ConcatAuthors(AuthorID)
FROM Questions
Solução
Dê uma olhada nesses artigos:
http://dataeducation.com/rowset-string-concatenation-which-method-is-best/
http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/ (Veja a solução de junção cruzada de Phil Factor nas respostas - que funcionarão no SQL Server 2000)
Obviamente, no SQL Server 2005, o truque para XML é mais fácil, mais flexível e geralmente mais desempenhado.
No que diz respeito ao retorno de um lixo para cada linha, se você ainda quiser fazer isso por algum motivo, poderá fazer isso em um procedimento armazenado, mas o cliente precisará consumir todas as linhas no primeiro lime e depois ir para o próximo linhas de linhas e associá -lo à primeira linha no primeiro lixo, etc. Seu SP precisaria abrir um cursor no mesmo conjunto que ele retornou como o primeiro lixo e executar várias seleções em sequência para gerar todas as linhas infantis. É uma técnica que eu fiz, mas apenas onde TUDO Os dados eram realmente necessários (por exemplo, em uma vista de árvore totalmente povoada).
E, independentemente do que as pessoas dizem, fazer isso ao lado do cliente geralmente é um grande desperdício de largura de banda, porque devolver todas as fileiras e fazer o loop e quebrar no lado do cliente significa que um grande número de colunas idênticas está sendo transferido no início do início do Cada linha apenas para obter a coluna de mudança no final da linha.
Onde quer que você faça isso, deve ser um decisão informada baseado em sua Caso de uso.
Outras dicas
Eu tentei 3 abordagens para esta solução, a uma postado aqui, activex, scripts e funções UDF.
O mais eficaz script (velocidade-wise) para mim foi bizzarely Axtive-X script executando várias consultas para obter o additioanl dados para concat.
UDF teve uma média de 22 minutos para transformar, a Subconsulta método (postado aqui) levou cerca de 5m e o script activeX tomou 4m30, tanto para o meu aborrecimento, já que este foi o script que eu estava esperando para vala.Eu vou ter que ver se eu consigo resolver alguns mais eficiência em outro lugar.
Eu acho que o adicional de 30, é usado pelo tempdb está sendo usada para armazenar os dados, pois o meu script requer um order by.
Deve ser notado que eu sou concatanating enormes quantidades de dados textuais.
Você também pode dar uma olhada nisso roteiro. É basicamente a abordagem de junção cruzada que Cade Roux também mencionou em seu post.
A abordagem acima parece muito limpa: você deve fazer uma exibição primeiro e em segundo lugar, criar uma instrução com base nos valores na visualização. A segunda instrução SQL que você pode construir dinamicamente em seu código, para que seja direto para o uso.
Não tenho certeza se isso funciona no SQL Server 2000, mas você pode tentar:
--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
Saída do 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)
EDITAR Consulta que substitui o caminho XML pelo XML RAW, então isso deve funcionar no 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
Saída, o mesmo que a consulta original