.NET : SQL  vs LINQ


 Hoje vamos comparar consultas SQL com as respectivas consultas LINQ

A escolha entre usar uma consulta SQL ou uma consulta LINQ depende do contexto em que você está trabalhando e das suas preferências pessoais.  A seguir veremos alguns exemplos de consultas LINQ e SQL mostrando as diferenças.

SQL (Structured Query Language):

LINQ (Language Integrated Query):

Se você estiver trabalhando exclusivamente com bancos de dados relacionais e desejar otimização de desempenho, consultas SQL podem ser mais adequadas. No entanto se você estiver desenvolvendo em uma linguagem de programação que suporta LINQ e desejar aproveitar a verificação de tipo e a integração com a linguagem, LINQ pode ser uma boa escolha.

A seguir para ilustrar vamos comparar algumas consultas LINQ com a respectiva consulta SQL

Os exemplos da LINQ mostrados a seguir pressupõem o uso do Entity Framework Core e um DbContext chamado db.

using (var db = new SeuDbContext()) 
{ 
   // ... as consultas LINQ são definidas aqui ... 
}

1- Select

Single : Seleciona apenas a propriedade Codigo de todos os registros da tabela Produto.

var resultado = db.Set<Produto>().Select(x => x.Codigo).ToList();
SELECT Codigo FROM Produto

Multiple : Seleciona as propriedades Codigo, Descricao e Preco de todos os registros da tabela Produto, criando um novo objeto anônimo para cada resultado.

var resultado = db.Set<Produto>()
  .Select(x => new
  {
    x.Codigo,
    x.Descricao,
    x.Preco
  }).ToList();
         SELECT Codigo, Descricao, Preco FROM Produto

All : Esta consulta LINQ seleciona todos os campos de todos os registros da tabela Produto.

var resultado = db.Set<Produto>().ToList();
SELECT * FROM Produto

Alias : Seleciona as propriedades Codigo e Descricao, e também calcula um novo valor chamado CustoTotal (multiplicando Estoque por Custo), atribuindo um alias a essa nova coluna no resultado.

var resultado = db.Set<Produto>()
  .Select(x => new
  {
    x.Codigo,
    x.Descricao,
    CustoTotal = x.Estoque * x.Custo
  }).ToList();
SELECT
  Codigo,
  Descricao,
  Estoque * Custo as CustoTotal
FROM Produto

2- Where

Igualdade : Filtra os registros da tabela Produto onde a propriedade Codigo é igual a "1001".

var resultado = db.Set<Produto>()
                         .Where(x => x.Codigo == "1001")
                         .ToList();
SELECT * FROM Produto
WHERE Codigo = '1001'

Contains/Like  : Filtra os registros da tabela Produto onde a propriedade Descricao contém a substring "MIE". Isso é geralmente traduzido para um operador LIKE no SQL.

var resultado = db.Set<Produto>()
                          .Where(x => x.Descricao.Contains("MIE"))
                         .ToList();
SELECT * FROM Produto
WHERE Nome LIKE '%MIE%'

StartsWith : Filtra os registros da tabela Produto onde a propriedade Descricao começa com a string "ABC". Isso é traduzido para um operador LIKE com um curinga no final no SQL.

var resultado = db.Set<Produto>()
                        .Where(x => x.Descricao.StartsWith("ABC"))
                        .ToList();
SELECT * FROM Produto
WHERE Codigo LIKE 'ABC%'

EndsWith : Filtra os registros da tabela Produto onde a propriedade Descricao termina com a string "ABC". Isso é traduzido para um operador LIKE com um curinga no início no SQL.

var resultado = db.Set<Produto>()
                        .Where(x => x.Descricao.EndsWith("ABC"))
                        .ToList();

SELECT * FROM Produto
WHERE Codigo LIKE '%ABC'

Contains/In : Filtra os registros da tabela Produto onde a propriedade Codigo está contida no array de strings arr. Isso é traduzido para um operador IN no SQL.

var arr = new string[] {"A001", "B001", "C001" };
var resultado = db.Set<Produto>()
                          .Where(x => arr.Contains(x.Codigo))
                          .ToList();
SELECT * FROM Produto
WHERE Codigo IN ('A001', 'B001', 'C001')

Intervalo de data : Filtra os registros da tabela Produto onde a parte da data da propriedade DataCriacao está dentro do intervalo especificado (de 01/01/2022 a 31/12/2022).

var inicio = new DateTime(2022, 1, 1);
var fim = new DateTime(2022, 12, 31);
var resultado = db.Set<Produto>()
                         .Where(x => x.DataCriacao.Date >= inicio && x.DataCriacao.Date <= fim)
                        .ToList();
SELECT * FROM Produto
WHERE DataCriacao >= '2022-01-01' AND DataCriacao <= '2022-12-31'

Count : Conta o número de registros na tabela Produto onde a propriedade Custo é maior que 1000.

var resultado = db.Set<Produto>()
                         .Count(x => x.Custo > 1000);
SELECT Count(*) FROM Produto WHERE Custo > 1000

Sum : Calcula a soma de todos os valores da propriedade Custo na tabela Produto.

var resultado = db.Set<Produto>()
                        .Sum(x => x.Custo);

SELECT SUM(Custo) FROM Produto

Min : Encontra o menor valor da propriedade Custo na tabela Produto.

var resultado = db.Set<Produto>()
                          .Min(x => x.Custo);

SELECT MIN(Custo) FROM Produto

Max : Encontra o maior valor da propriedade Custo na tabela Produto.

var resultado = db.Set<Produto>()
                         .Max(x => x.Custo);
SELECT MAX(Custo) FROM Produto

Average : Calcula a média de todos os valores da propriedade Custo na tabela Produto.

var resultado = db.Set<Produto>()
                         .Average(x => x.Custo);
SELECT Average(Custo) FROM Produto

3- Order

Ascending : Ordena todos os registros da tabela Produto pela propriedade Codigo em ordem crescente (padrão).

var resultado = db.Set<Produto>()
                         .OrderBy(x => x.Codigo)
                          .ToList();
SELECT * FROM Produto
ORDER BY Codigo

Descending : Ordena todos os registros da tabela Produto pela propriedade Codigo em ordem decrescente.

var resultado = db.Set<Produto>()
                        .OrderByDesc(x => x.Codigo)
                       .ToList();
SELECT * FROM Produto
ORDER BY Codigo DESC

Multiple : Ordena todos os registros da tabela Produto primeiramente pela propriedade Codigo (ascendente) e, em seguida, pela propriedade Descricao (ascendente) dentro dos grupos de Codigo.

var resultado = db.Set<Produto>()
                        .OrderBy(x => x.Codigo)
                        .ThenBy(x => x.Descricao)
                       .ToList();
SELECT * FROM Produto
ORDER BY Codigo, Descricao

4- Group

Single : Agrupa os registros da tabela Produto pela propriedade Categoria e, para cada categoria, seleciona a categoria e a soma dos custos dos produtos nessa categoria.

var resultado = db.Set<Produto>()
                         .GroupBy(x => x.Categoria)
                         .Select(x => new
                         {
                             Categoria = x.Key,
                             CustoTotal = x.Sum(a => a.Custo)
                         }).ToList();
SELECT Categoria, SUM(Custo) as CustoTotal
FROM Produto
GROUP BY Categoria

Multiple : Agrupa os registros da tabela Produto pelas propriedades Categoria e Fornecedor, e para cada combinação única, seleciona a categoria, o fornecedor e a soma dos custos dos produtos nessa combinação.

var resultado = db.Set<Produto>()
  .GroupBy(x => new
  {
    x.Categoria,
    x.Fornecedor
  })
  .Select(x => new
  {
    Categoria = x.Key,
    CustoTotal = x.Sum(a => a.Custo)
  }).ToList();
SELECT Categoria, Fornecedor, SUM(Custo) as CustoTotal
FROM Produto
GROUP BY Categoria, Fornecedor

Paging : Implementa a paginação. Primeiro, ordena os produtos por Codigo, depois pula um certo número de registros (skip) com base na página atual e no tamanho da página, e finalmente pega um número específico de registros (take) para exibir na página atual.

var pagina = 3;
var tamanho = 10;
var skip = (pagina - 1) * tamanho;
var take = tamanho;

var resultado = db.Set<Produto>()
  .OrderBy(x => x.Codigo)
  .Skip(skip)
  .Take(take)
  .ToList();
SELECT * FROM Produto
ORDER BY Codigo
OFFSET 20 ROWS
FETCH NEXT 10 ROWS ONLY

Join : Realiza um join implícito entre as tabelas Pedido, Cliente e Produto (assumindo que as propriedades de navegação Cliente e Produto estão definidas na entidade Pedido). Ela seleciona a data da transação do pedido, o nome do cliente e o código do produto.

var resultado = db.Set<Pedido>()
  .Select(x => new
  {
    x.DataTransacao,
    NomeCliente = x.Cliente.Nome,
    ProdutoCodigo = x.Produto.Codigo,
    x.Total
  }).ToList();
SELECT
  s.DataTransacao,
  c.Nome as NomeCliente,
  p.Codigo as ProdutoCodigo
FROM Pedido s
LEFT JOIN Cliente c ON s.ClienteId = c.Id
LEFT JOIN Produto p ON s.ProdutoId = p.Id

5- Consulta complexa : Demonstra uma consulta mais complexa que envolve filtragem por um intervalo de datas, agrupamento por nome do cliente e código do produto, cálculo do total de vendas para cada grupo e, finalmente, ordenação pelos nomes dos clientes em ordem crescente e pelos códigos dos produtos em ordem decrescente dentro de cada cliente.

var inicio = new DateTime(2022, 1, 1);
var fim = new DateTime(2022, 12, 31);
var resultado = db.Set<Pedido>()
  .Where(x.DataTransacao >= inicio && x.DataTransacao <= fim)
  .GroupBy(x => new
  {
    NomeCliente = x.Cliente.Nome,
    ProdutoCodigo = x.Produto.Codigo
  })
  .Select(x => new
  {
    NomeCliente = x.Key.NomeCliente,
    ProdutoCodigo = x.Key.ProdutoCodigo,
    TotalVendas = x.Sum(x.Total)
  })
  .OrderBy(x => x.NomeCliente)
  .ThenByDescending(x => x.ProdutoCodigo)
  .ToList();
SELECT
  c.Nome as NomeCliente,
  p.Codigo as ProdutoCodigo,
  SUM(s.Total) AS TotalVendas
FROM Pedido s
LEFT JOIN Cliente c ON s.ClienteId = c.Id
LEFT JOIN Produto p ON s.ProdutoId = p.Id
WHERE s.DataTransacao >= '2022-01-01'
  AND s.DataTransacao <= '2022-12-31'
GROUP By c.Nome, p.Codigo
ORDER BY c.Nome, p.Codigo DESC

Para consultas muito complexas envolvendo múltiplas junções e subconsultas, o SQL pode oferecer mais controle e potencial de otimização manual. No entanto, o EF Core tem evoluído bastante na tradução de LINQ para SQL eficiente.

E estamos conversados...

"E disse-lhe Jesus: Em verdade te digo que hoje estarás comigo no Paraíso."
Lucas 23:43

Referências:


José Carlos Macoratti