Eu tenho uma tabela na qual quero obter a entrada mais recente para cada grupo. Aqui está a tabela:
DocumentStatusLogs
Mesa
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
A tabela será agrupada DocumentID
e classificada por DateCreated
ordem decrescente. Para cada um DocumentID
, quero obter o status mais recente.
Minha saída preferida:
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
Existe alguma função agregada para obter apenas o topo de cada grupo? Veja pseudo-código
GetOnlyTheTop
abaixo:SELECT DocumentID, GetOnlyTheTop(Status), GetOnlyTheTop(DateCreated) FROM DocumentStatusLogs GROUP BY DocumentID ORDER BY DateCreated DESC
Se essa função não existir, existe alguma maneira de obter a saída desejada?
- Ou, em primeiro lugar, isso poderia ser causado por banco de dados não normalizado? Estou pensando, já que o que estou procurando é apenas uma linha, isso
status
também deve estar localizado na tabela pai?
Consulte a tabela pai para obter mais informações:
Documents
Tabela Atual
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
A tabela pai deve ser assim para que eu possa acessar facilmente seu status?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
ATUALIZAÇÃO Acabei de aprender a usar "aplicar", o que facilita a solução desses problemas.
Respostas:
Se você espera duas entradas por dia, isso selecionará arbitrariamente uma. Para obter as duas entradas de um dia, use DENSE_RANK.
Quanto à normalização ou não, depende se você deseja:
Tal como está, você preserva o histórico de status. Se você também deseja o status mais recente na tabela pai (que é desnormalização), precisará de um gatilho para manter o "status" no pai. ou solte esta tabela do histórico de status.
fonte
Partition By
?With
é novo para mim também :( Eu estou usando MSSQL 2005 de qualquer maneira.ROW_NUMBER
algum tipo de subconsulta para cada linha?Acabei de aprender a usar
cross apply
. Veja como usá-lo neste cenário:fonte
Fiz alguns ajustes nas várias recomendações aqui, e os resultados realmente dependem do tamanho da tabela envolvida, mas a solução mais consistente é usar o CROSS APPLY. Esses testes foram executados no SQL Server 2008-R2, usando uma tabela com 6.500 registros e outro (esquema idêntico) com 137 milhões de registros. As colunas que estão sendo consultadas fazem parte da chave primária da tabela e a largura da tabela é muito pequena (cerca de 30 bytes). Os horários são relatados pelo SQL Server a partir do plano de execução real.
Eu acho que o mais incrível foi a consistência do tempo para o CROSS APPLY, independentemente do número de linhas envolvidas.
fonte
Eu sei que esse é um tópico antigo, mas as
TOP 1 WITH TIES
soluções são bastante boas e podem ser úteis para algumas leituras das soluções.Mais informações sobre a cláusula TOP podem ser encontradas aqui .
fonte
Se você está preocupado com o desempenho, também pode fazer isso com o MAX ():
ROW_NUMBER () requer uma espécie de todas as linhas na sua instrução SELECT, enquanto MAX não. Deve acelerar drasticamente sua consulta.
fonte
row_number()
mesmo com a indexação adequada. Acho isso especialmente valioso em cenários de auto-junção. Porém, o que você deve saber é que esse método geralmente gera um número maior de leituras lógicas e contagens de varredura, apesar de relatar um baixo custo de subárvore. Você precisará pesar os custos / benefícios em seu caso específico para determinar se é realmente melhor.Qual servidor de banco de dados? Este código não funciona em todos eles.
Em relação à segunda metade da sua pergunta, parece-me razoável incluir o status como uma coluna. Você pode sair
DocumentStatusLogs
como um log, mas ainda assim armazenar as informações mais recentes na tabela principal.BTW, se você já possui a
DateCreated
coluna na tabela Documentos, você pode simplesmente ingressarDocumentStatusLogs
usando isso (desde queDateCreated
seja exclusivoDocumentStatusLogs
).Edit: MsSQL não suporta USING, então mude para:
fonte
max(DateCreated)
Essa é uma das perguntas mais facilmente encontradas sobre o assunto, então eu queria dar uma resposta moderna para ele (tanto para minha referência quanto para ajudar outras pessoas). Usando
first_value
eover
você pode fazer um breve trabalho na consulta acima:Isso deve funcionar no Sql Server 2008 e superior.
First_value
pode ser pensado como uma maneira de realizarSelect Top 1
ao usar umaover
cláusula.Over
permite agrupar na lista de seleção e, em vez de escrever subconsultas aninhadas (como muitas das respostas existentes), isso é feito de maneira mais legível. Espero que isto ajude.fonte
Esse é um tópico bastante antigo, mas achei que eu jogaria meus dois centavos da mesma forma que a resposta aceita não funcionou particularmente bem para mim. Tentei a solução da gbn em um grande conjunto de dados e a achei muito lenta (> 45 segundos em mais de 5 milhões de registros no SQL Server 2012). Observando o plano de execução, é óbvio que o problema é que ele requer uma operação SORT que torna as coisas mais lentas.
Aqui está uma alternativa que levantei da estrutura da entidade que não precisa de operação SORT e faz uma pesquisa de índice não clusterizado. Isso reduz o tempo de execução para <2 segundos no conjunto de registros mencionado acima.
Agora, estou assumindo algo que não está totalmente especificado na pergunta original, mas se o design da tabela for tal que sua coluna de ID seja uma ID de incremento automático e o DateCreated esteja definido para a data atual com cada inserção, sem executar a minha consulta acima, você pode obter um aumento considerável no desempenho da solução da gbn (cerca de metade do tempo de execução) apenas solicitando o ID em vez de o DateCreated, pois isso fornecerá uma ordem de classificação idêntica e é mais rápida.
fonte
Meu código para selecionar os 1 primeiros de cada grupo
fonte
Verificando a resposta impressionante e correta de Clint acima:
O desempenho entre as duas consultas abaixo é interessante. 52% sendo o primeiro. E 48% é o segundo. Uma melhoria de 4% no desempenho usando DISTINCT em vez de ORDER BY. Mas ORDER BY tem a vantagem de classificar por várias colunas.
Opção 1:
Opção 2:
M $ 's Management Studio: Após destacar e executar o primeiro bloco, realce as opções 1 e 2, clique com o botão direito do mouse em -> [Exibir plano de execução estimado]. Em seguida, execute a coisa toda para ver os resultados.
Resultados da opção 1:
Resultados da opção 2:
Nota:
Também evito subconsultas EXISTS / IN na cláusula WHERE ou ON, pois experimentei isso causando alguns planos de execução terríveis. Mas a milhagem varia. Revise o plano de execução e o desempenho do perfil onde e quando necessário!
fonte
Esta solução pode ser usada para obter as TOP N linhas mais recentes de cada partição (no exemplo, N é 1 na instrução WHERE e a partição é doc_id):
fonte
Se você deseja devolver apenas o pedido recente de documento por DateCreated, ele retornará apenas o documento principal 1 por DocumentID
fonte
CROSS APPLY
foi o método que usei para minha solução, pois funcionou para mim e para as necessidades de meus clientes. E pelo que li, deve fornecer o melhor desempenho geral, caso o banco de dados cresça substancialmente.fonte
Aqui estão três abordagens separadas para o problema em mãos, juntamente com as melhores opções de indexação para cada uma dessas consultas (tente você mesmo os índices e veja a leitura lógica, o tempo decorrido, o plano de execução. Forneci as sugestões da minha experiência em consultas sem executar para esse problema específico).
Abordagem 1 : usando ROW_NUMBER (). Se o índice rowstore não conseguir melhorar o desempenho, você poderá experimentar o índice columnstore não clusterizado / em cluster como para consultas com agregação e agrupamento e para tabelas ordenadas por colunas diferentes o tempo todo, o índice columnstore geralmente é a melhor escolha.
Abordagem 2 : usando FIRST_VALUE. Se o índice rowstore não conseguir melhorar o desempenho, você poderá experimentar o índice columnstore não clusterizado / em cluster como para consultas com agregação e agrupamento e para tabelas ordenadas por colunas diferentes o tempo todo, o índice columnstore geralmente é a melhor escolha.
Abordagem 3 : Usando CROSS APPLY. A criação do índice rowstore na tabela DocumentStatusLogs que cobre as colunas usadas na consulta deve ser suficiente para cobrir a consulta sem a necessidade de um índice columnstore.
fonte
Eu acredito que isso pode ser feito assim. Isso pode precisar de alguns ajustes, mas você pode apenas selecionar o máximo no grupo.
Essas respostas são um exagero.
fonte
Nos cenários em que você deseja evitar o uso de row_count (), você também pode usar uma junção esquerda:
Para o esquema de exemplo, você também pode usar uma "não na subconsulta", que geralmente compila a mesma saída que a junção esquerda:
Observe que o padrão de subconsulta não funcionaria se a tabela não tivesse pelo menos uma chave / restrição / índice exclusivos de coluna única; nesse caso, a chave primária "Id".
Ambas as consultas tendem a ser mais "caras" que a consulta row_count () (conforme medida pelo Query Analyzer). No entanto, você pode encontrar cenários em que eles retornam resultados mais rapidamente ou ativam outras otimizações.
fonte
fonte
Tente o seguinte:
fonte
Este é o TSQL mais baunilha que eu posso criar
fonte
É verificado no SQLite que você pode usar a seguinte consulta simples com GROUP BY
Aqui, o MAX ajuda a obter o máximo de DateCreated FROM de cada grupo.
Mas parece que o MYSQL não associa * -columns ao valor de max DateCreated :(
fonte