Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Matthieu MEZIL

I love .Net

Abonnements

Petit quizz LINQ To SQL

Le code suivant :

using (var context = new DataClasses1DataContext())

{

    int[] keyValues = { 1, 2, 3 };

    var q = from p in context.Products

            where keyValues.Contains(p.ProductID)

            select p;

    var r = new

    {

        Count = q.Count(),

        CountCategory = (from p in q

                        select p.CategoryID).Distinct().Count(),

        AverageUnitPrice = (from p in q

                            select p.UnitPrice).Average()

    };

    // Do something with r

}

 

a le défaut majeur de générer 3 requêtes SQL :

 

SELECT COUNT(*) AS [value]
FROM [dbo].[Products] AS [t0]
WHERE [t0].[ProductID] IN (@p0, @p1, @p2)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [3]

 

SELECT COUNT(*) AS [value]
FROM (
    SELECT DISTINCT [t0].[CategoryID]
    FROM [dbo].[Products] AS [t0]
    WHERE [t0].[ProductID] IN (@p0, @p1, @p2)
    ) AS [t1]
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [3]

 

SELECT AVG([t0].[UnitPrice]) AS [value]
FROM [dbo].[Products] AS [t0]
WHERE [t0].[ProductID] IN (@p0, @p1, @p2)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [3]


 

Comment faire en sorte de n'exécuter qu'une seule requête SQL ?

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Publié lundi 10 mars 2008 08:13 par Matthieu MEZIL

Classé sous : , , , ,

Commentaires

# re: Petit quizz LINQ To SQL @ lundi 10 mars 2008 19:19

J'avoue que je ne vois pas la solution.

Enfin j'en vois une mauvaise qui serait de forcer l'execution de q avant d'appeller les Count et Average:

var q = (from p in context.Products

           where keyValues.Contains(p.ProductID)

           select p).ToArray();

par exemple.

Apres évidement, les Count et Average sont executes par Linq to Object et ne génèrent donc plus de requetes SQL. Mais c'est une mauvaise solution: ca serait bête d'être obligé de récupérer 1 million de lignes pour juste faire un count et average, alors que ca aurait pu etre fait coté SQL Server: gachis de mémoire, de bande passante et de CPU.

Je suis intéressé par la solution en tout cas :)

RaptorXP

# re: Petit quizz LINQ To SQL @ lundi 10 mars 2008 22:00

Effectivement ce n'est pas la meilleure solution car cela implique de récupérer l'ensemble des enregistrements de la base vers la mémoire.

Personne d'autre n'a d'idée ? ;-)

Matthieu MEZIL

# re: Petit quizz LINQ To SQL @ lundi 10 mars 2008 22:55

Bon alors, c'est quoi la solution ? :)

Thomas LEBRUN

# re: Petit quizz LINQ To SQL @ lundi 10 mars 2008 23:52

int[] keyValues = { 1, 2, 3 };

var q = from gp in

           (from p in ctx.Products

            where keyValues.Contains(p.ProductID)

            group p by p.CategoryID into groupOfProds

            select new

            {

                ProductCount = groupOfProds.Count(),

                TotalUnitPrice = groupOfProds.Sum(prod => prod.UnitPrice)

            })

       group gp by 0 into gp2

       select new

       {

           CategoryCount = gp2.Count(),

           Count = gp2.Sum(gp => gp.ProductCount),

           AverageUnitPrice = gp2.Sum(gp => gp.TotalUnitPrice) / gp2.Sum(gp => gp.ProductCount)

       };

Console.WriteLine(q.FirstOrDefault());

Enjoy!

Le soucis c'est les deux niveaux d'agrégation dans la même requête, et surtout, l'aggrégation sur les catégories (qui doit retourner qu'une seule ligne). La grosse feinte dans ma requete, c'est le group gp by 0 into gp2 qui me permet d'aggréger les données de toutes les catégories dans une seule ligne ^^

simon ferquel

# re: Petit quizz LINQ To SQL @ mardi 11 mars 2008 13:01

En SQL c'est plus simple :

SELECT COUNT(*) AS [value1], AVG([t0].[UnitPrice]) AS [value2], COUNT(DISTINCT [CategoryID])  AS [value3]

FROM [dbo].[Products] AS [t0]

WHERE [t0].[ProductID] IN (@p0, @p1, @p2)

!!!

christian

# re: Petit quizz LINQ To SQL @ mardi 11 mars 2008 13:14

Ok, ca a l'air quand meme un peu trop complique, personellement je préfererais encore faire 3 requetes et avoir un code plus maintenable. Mais c'est bien de savoir que c'est possible.

A mon avis, il faut éviter les chargements différées surtout dans les boucles, mais quand le nombre de requetes supplémentaires est constant (ici 3 au lieu d'une), je ne pense pas que les performances ne soit vraiment impactées.

RaptorXP

# re: Petit quizz LINQ To SQL @ mardi 11 mars 2008 17:36

Pas mal Simon. Effectivement, l'idée est de faire un group by sur une constante. Moi j'avais pensé à cette requête :

var q2 = (from p in context.Products

         where keyValues.Contains(p.ProductID)

         group p by 0 into g

         select new { Count = g.Count(), CountCategory = g.Select(p => p.CategoryID).Distinct().Count(), AverageUnitPrice = g.Average(p => p.UnitPrice) }).First();

Cependant la requête générée :

SELECT TOP (1) [t5].[value] AS [Count], [t5].[value2] AS [CountCategory], [t5].[value22] AS [AverageUnitPrice]

FROM (

   SELECT [t2].[value], (

       SELECT COUNT(*)

       FROM (

           SELECT DISTINCT [t3].[CategoryID]

           FROM [dbo].[Products] AS [t3]

           WHERE ([t2].[value3] = @p4) AND ([t3].[ProductID] IN (@p1, @p2, @p3))

           ) AS [t4]

       ) AS [value2], [t2].[value2] AS [value22]

   FROM (

       SELECT COUNT(*) AS [value], AVG([t1].[UnitPrice]) AS [value2], [t1].[value] AS [value3]

       FROM (

           SELECT @p0 AS [value], [t0].[ProductID], [t0].[UnitPrice]

           FROM [dbo].[Products] AS [t0]

           ) AS [t1]

       WHERE [t1].[ProductID] IN (@p1, @p2, @p3)

       GROUP BY [t1].[value]

       ) AS [t2]

   ) AS [t5]

n'est pas optimale. Dans ce cas là, il peut être intéressant de se créer une fonction sql avec la requête triviale donnée par Christian et de l'importer dans notre DataContext.

Matthieu MEZIL

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- TechDays Paris 2010 : La BI dans SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 1 heure et 8 minutes

- TechDays Paris 2010 : Déploiement de nouvelles technologies – Retour d’expérience par l’informatique de Microsoft par Blog Technique de Romelard Fabrice le il y a 2 heures et 35 minutes

- TechDays Paris 2010 : Plan de migration vers SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 6 heures et 18 minutes

- TechDays Paris 2010 : La pleinière du second jour par Blog Technique de Romelard Fabrice le il y a 7 heures et 23 minutes

- Visual Studio 2010 and .NET Framework 4 Release Candidate now available par Matthieu MEZIL le il y a 10 heures et 29 minutes

- Création d’une base de donnée sous SQL Azure par Le Blog (Vert) d'Arnaud JUND le il y a 11 heures et 25 minutes

- TechDays Paris 2010 : Les Services d’applications dans SharePoint 2010 par Blog Technique de Romelard Fabrice le il y a 21 heures et 25 minutes

- TechDays Paris 2010 : La GED et SharePoint 2010 par Blog Technique de Romelard Fabrice le 02-08-2010, 16:54

- TechDays Paris 2010 : SharePoint 2010 et Les réseaux sociaux par Blog Technique de Romelard Fabrice le 02-08-2010, 15:40

- TechDays Paris 2010 : SharePoint 2010 – Description et nouveautés par Blog Technique de Romelard Fabrice le 02-08-2010, 14:33