LINQ ORDENAR E AGRUPAR PELO MAIOR

MAXCIM 26/04/2022 11:51:06
#499715
olá amigos,

tenho uma lista, com as ultimas vendas, por produto x Cliente.

preciso filtrar a ultima data ( ultima venda) de um produto e cliente , segue a imagem da lista.

obs, tentei fazer esse filtro por SQL direto no mysql, porem demorou muito e chegou a derrubar o servidor.

KERPLUNK 26/04/2022 13:50:07
#499716
Voce está usando uma ferramenta que faz conversões entre Lambda, LINQ e SQL.
Se souber fazer o que quer no SQL, basta fazer a conversão.
MAXCIM 26/04/2022 16:05:09
#499718
KERPLUNK, quando postei aqui, é porque já tinha esgotado todas a minhas tentativas, dai limpei os testes e deixei só a tabela.

o filtro .where(funcion(F) ... buscando pelo IDCLI e IDPRO eu consegui.

mas pegar a maior data ainda não deu certo.



ou nem fazer o filtro de IDCLI e IDPRO, apenas agrupar pelo IDCLI e IDPRO e exibir a ultima data.

por isso peço ajuda.

DS2T 27/04/2022 18:24:03
#499733
Resposta escolhida
Segue um exemplo. Neste exemplo eu mostro tanto usando lambda quanto linq to sql. Ambos deveriam dar o mesmo resultado.
No caso estou pegando a última venda realizada de um produto por determinado cliente. (Agrupando por cliente e produto e pegando o max da data)


using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqExemploMaxcim
{
class Venda
{
public int IdCliente { get; set; }
public int IdProduto { get; set; }
public DateTime DataVenda { get; set; }
}

static class FabricaVenda
{
internal static IEnumerable<Venda> CriaColecao()
{
return new List<Venda>()
{
new Venda() { IdCliente = 1, IdProduto = 1, DataVenda = new DateTime(2022, 04, 10) },
new Venda() { IdCliente = 1, IdProduto = 1, DataVenda = new DateTime(2022, 04, 03) },
new Venda() { IdCliente = 1, IdProduto = 1, DataVenda = new DateTime(2022, 04, 06) },
new Venda() { IdCliente = 1, IdProduto = 2, DataVenda = new DateTime(2022, 04, 09) },
new Venda() { IdCliente = 1, IdProduto = 3, DataVenda = new DateTime(2022, 04, 15) },
new Venda() { IdCliente = 2, IdProduto = 3, DataVenda = new DateTime(2022, 04, 20) },
new Venda() { IdCliente = 2, IdProduto = 2, DataVenda = new DateTime(2022, 04, 11) },
new Venda() { IdCliente = 2, IdProduto = 2, DataVenda = new DateTime(2022, 04, 17) },
new Venda() { IdCliente = 2, IdProduto = 2, DataVenda = new DateTime(2022, 04, 10) },
};
}
}

class Program
{
static void Main(string[] args)
{
IEnumerable<Venda> vendas = FabricaVenda.CriaColecao();

//VOCÊ PODE USAR VIA LAMBDA
IEnumerable<Venda> ultimasVendasPorClienteProdutoViaLambda = vendas
.GroupBy(venda => new { venda.IdCliente, venda.IdProduto })
.Select(venda => new Venda()
{
IdCliente = venda.Key.IdCliente,
IdProduto = venda.Key.IdProduto,
DataVenda = venda.Max(v => v.DataVenda)
});

//MAS O LINQ TO SQL DEVERIA FUNCIONAR TAMBÉM
IEnumerable<Venda> ultimasVendasPorClienteProdutoViaSql = from venda in vendas
group venda by new { venda.IdCliente, venda.IdProduto } into grupo
select new Venda()
{
IdCliente = grupo.Key.IdCliente,
IdProduto = grupo.Key.IdProduto,
DataVenda = grupo.Max(v => v.DataVenda)
};
}

}
}



Sobre derrubar o servidor:

Sempre é bom verificar a consulta que está indo pro banco de dados. Dependendo do mapeamento pode ter uma conversão implícita que pode acabar com sua performance por não usar o índice;
Verifique o plano de execução e veja se está realizando as operações desejadas, assim como as unidades de custo de processamento e leitura de disco.
Crie índices, se necessário;
Crie partições, se necessário;


Abraços!
MAXCIM 28/04/2022 15:02:01
#499745
DS2t, excelente sua dica, já apliquei aqui.
e isso me empolgou a reduzir o processo, a uma única consulta , ( antes fazia duas, uma com todos os dados , e outra agrupada com ultima data. e filtrando pela maior data.)


porem to com um erro no filtro dentro do grupo. segue imagem.
MAXCIM 28/04/2022 16:15:19
#499746
consegui executar, mas.. não buscar a informação de ultimo pedido e nem o valor.

e demorou quase 20 minutos para rodar. ( a consulta VIEW retorna 1800 linhas)
DS2T 30/04/2022 03:23:08
#499753
Não tinha visto que era VB.NET, foi mal ter colocado em C#. Fiz um exemplo em VB, mas te falar que a sintaxe do LINQ no VB é doída.



Module Program

Public Class Venda
Public Property IdCliente As Integer
Public Property IdProduto As Integer
Public Property DataVenda As DateTime
Public Property CodigoPedido As String
Public Property Valor As Decimal

End Class

Class FabricaVenda

Friend Shared Function CriaColecao() As IEnumerable(Of Venda)
Return New List(Of Venda) From
{
New Venda() With {.IdCliente = 1, .IdProduto = 1, .DataVenda = New DateTime(2022, 4, 1), .CodigoPedido = "A11", .Valor = 22},
New Venda() With {.IdCliente = 1, .IdProduto = 1, .DataVenda = New DateTime(2022, 4, 2), .CodigoPedido = "A12", .Valor = 12},
New Venda() With {.IdCliente = 1, .IdProduto = 1, .DataVenda = New DateTime(2022, 4, 3), .CodigoPedido = "A13", .Valor = 62},
New Venda() With {.IdCliente = 1, .IdProduto = 2, .DataVenda = New DateTime(2022, 4, 4), .CodigoPedido = "A14", .Valor = 21},
New Venda() With {.IdCliente = 1, .IdProduto = 3, .DataVenda = New DateTime(2022, 4, 5), .CodigoPedido = "A15", .Valor = 23},
New Venda() With {.IdCliente = 1, .IdProduto = 3, .DataVenda = New DateTime(2022, 4, 6), .CodigoPedido = "A16", .Valor = 25},
New Venda() With {.IdCliente = 2, .IdProduto = 1, .DataVenda = New DateTime(2022, 4, 7), .CodigoPedido = "A17", .Valor = 43},
New Venda() With {.IdCliente = 2, .IdProduto = 1, .DataVenda = New DateTime(2022, 4, 8), .CodigoPedido = "A18", .Valor = 31},
New Venda() With {.IdCliente = 2, .IdProduto = 2, .DataVenda = New DateTime(2022, 4, 9), .CodigoPedido = "A19", .Valor = 65},
New Venda() With {.IdCliente = 2, .IdProduto = 2, .DataVenda = New DateTime(2022, 4, 8), .CodigoPedido = "B11", .Valor = 41}}
End Function


End Class


Sub Main(args As String())
Dim vendas As IQueryable(Of Venda) = FabricaVenda.CriaColecao().AsQueryable

Dim queryableUltimasVendas As IQueryable(Of Venda) = vendas.
GroupBy(Function(venda) New With {Key venda.IdCliente, Key venda.IdProduto}).
Select(Function(venda) New Venda() With {
.IdCliente = venda.Key.IdCliente,
.IdProduto = venda.Key.IdProduto,
.DataVenda = venda.Max(Function(v) v.DataVenda)})


Dim queryFinal As IQueryable(Of Venda) = From venda In vendas Join
ultimaVenda In queryableUltimasVendas On
New With {Key venda.IdCliente, Key venda.IdProduto, Key venda.DataVenda} Equals
New With {Key ultimaVenda.IdCliente, Key ultimaVenda.IdProduto, Key ultimaVenda.DataVenda}
Select venda

Dim resultado As IEnumerable(Of Venda) = queryFinal.ToList()


End Sub
End Module



Repare que estou usando um IQueryable, ao invés de uma List ou IEnumerable. IQueryable representa uma consulta que ainda não foi processada. Apenas a partir do momento que eu faço o ToList no final, que vai no banco de dados. Dava até para fazer em uma só, mas acho que ficaria muito confuso, até pela sintaxe do VB.NET.
Agora, o que essa consulta vai renderizar, vai depender do seu provedor de conexão com o banco e a versão do EF.

Mas essa seria uma ideia.


Ah, eu também criaria um índice no campo DataVenda, vai ajudar bastante.

Até

MAXCIM 15/05/2022 15:54:08
#499842
Obrigado DS2T, funcionou direitinho. valeu
Tópico encerrado , respostas não são mais permitidas