欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

LINQtoSQL语句中Join和SelectMany的相关操作

程序员文章站 2022-07-01 18:21:27
join操作 适用场景:在我们表关系中有一对一关系,一对多关系,多对多关系等。对各个表之间的关系,就用这些实现对多个表的操作。 说明:在join操作中,分别为join(join查询), select...

join操作

适用场景:在我们表关系中有一对一关系,一对多关系,多对多关系等。对各个表之间的关系,就用这些实现对多个表的操作。

说明:在join操作中,分别为join(join查询), selectmany(select一对多选择)和groupjoin(分组join查询)。

该扩展方法对两个序列中键匹配的元素进行inner join操作

selectmany

说明:我们在写查询语句时,如果被翻译成selectmany需要满足2个条件。1:查询语句中没有join和into,2:必须出现entityset。在我们表关系中有一对一关系,一对多关系,多对多关系等,下面分别介绍一下。

1.一对多关系(1 to many):

var q =
    from c in db.customers
    from o in c.orders
    where c.city == "london"
    select o;

语句描述:customers与orders是一对多关系。即orders在customers类中以entityset形式出现。所以第二个from是从c.orders而不是db.orders里进行筛选。这个例子在from子句中使用外键导航选择伦敦客户的所有订单。

var q =
    from p in db.products
    where p.supplier.country == "usa" && p.unitsinstock == 0
    select p;

语句描述:这一句使用了p.supplier.country条件,间接关联了supplier表。这个例子在where子句中使用外键导航筛选其供应商在美国且缺货的产品。生成sql语句为:

select [t0].[productid], [t0].[productname], [t0].[supplierid],
[t0].[categoryid],[t0].[quantityperunit],[t0].[unitprice], 
[t0].[unitsinstock], [t0].[unitsonorder],[t0].[reorderlevel],
[t0].[discontinued] from [dbo].[products] as [t0]
left outer join [dbo].[suppliers] as [t1] on 
[t1].[supplierid] = [t0].[supplierid]
where ([t1].[country] = @p0) and ([t0].[unitsinstock] = @p1)
-- @p0: input nvarchar (size = 3; prec = 0; scale = 0) [usa]
-- @p1: input int (size = 0; prec = 0; scale = 0) [0]

2.多对多关系(many to many):

var q =
    from e in db.employees
    from et in e.employeeterritories
    where e.city == "seattle"
    select new
    {
        e.firstname,
        e.lastname,
        et.territory.territorydescription
    };

说明:多对多关系一般会涉及三个表(如果有一个表是自关联的,那有可能只有2个表)。这一句语句涉及employees, employeeterritories, territories三个表。它们的关系是1:m:1。employees和territories没有很明确的关系。

语句描述:这个例子在from子句中使用外键导航筛选在西雅图的雇员,同时列出其所在地区。这条生成sql语句为:

select [t0].[firstname], [t0].[lastname], [t2].[territorydescription]
from [dbo].[employees] as [t0] cross join [dbo].[employeeterritories]
as [t1] inner join [dbo].[territories] as [t2] on 
[t2].[territoryid] = [t1].[territoryid]
where ([t0].[city] = @p0) and ([t1].[employeeid] = [t0].[employeeid])
-- @p0: input nvarchar (size = 7; prec = 0; scale = 0) [seattle]

3.自联接关系:

var q =
    from e1 in db.employees
    from e2 in e1.employees
    where e1.city == e2.city
    select new {
        firstname1 = e1.firstname, lastname1 = e1.lastname,
        firstname2 = e2.firstname, lastname2 = e2.lastname,
        e1.city
    };

语句描述:这个例子在select 子句中使用外键导航筛选成对的雇员,每对中一个雇员隶属于另一个雇员,且两个雇员都来自相同城市。生成sql语句为:

select [t0].[firstname] as [firstname1], [t0].[lastname] as 
[lastname1],[t1].[firstname] as [firstname2], [t1].[lastname] as 
[lastname2],[t0].[city] from [dbo].[employees] as [t0],
[dbo].[employees] as [t1] where ([t0].[city] = [t1].[city]) and 
([t1].[reportsto] = [t0].[employeeid])

groupjoin

像上面所说的,没有join和into,被翻译成selectmany,同时有join和into时,那么就被翻译为groupjoin。在这里into的概念是对其结果进行重新命名。

1.双向联接(two way join):

此示例显式联接两个表并从这两个表投影出结果:

var q =
    from c in db.customers
    join o in db.orders on c.customerid
    equals o.customerid into orders
    select new
    {
        c.contactname,
        ordercount = orders.count()
    };

说明:在一对多关系中,左边是1,它每条记录为c(from c in db.customers),右边是many,其每条记录叫做o ( join o in db.orders ),每对应左边的一个c,就会有一组o,那这一组o,就叫做orders,也就是说,我们把一组o命名为orders,这就是into用途。这也就是为什么在select语句中,orders可以调用聚合函数count。在t-sql中,使用其内嵌的t-sql返回值作为字段值。如图所示:

LINQtoSQL语句中Join和SelectMany的相关操作

生成sql语句为:

select [t0].[contactname], (
    select count(*)
    from [dbo].[orders] as [t1]
    where [t0].[customerid] = [t1].[customerid]
) as [ordercount]
from [dbo].[customers] as [t0]

2.三向联接(there way join):

此示例显式联接三个表并分别从每个表投影出结果:

var q =
    from c in db.customers
    join o in db.orders on c.customerid
    equals o.customerid into ords
    join e in db.employees on c.city
    equals e.city into emps
    select new
    {
        c.contactname,
        ords = ords.count(),
        emps = emps.count()
    };

生成sql语句为:

select [t0].[contactname], (
    select count(*)
    from [dbo].[orders] as [t1]
    where [t0].[customerid] = [t1].[customerid]
) as [ords], (
select count(*)
    from [dbo].[employees] as [t2]
    where [t0].[city] = [t2].[city]
) as [emps]
from [dbo].[customers] as [t0]

3.左外部联接(left outer join):

此示例说明如何通过使用 此示例说明如何通过使用defaultifempty() 获取左外部联接。在雇员没有订单时,defaultifempty()方法返回null:

var q =
    from e in db.employees
    join o in db.orders on e equals o.employee into ords
    from o in ords.defaultifempty()
    select new
    {
        e.firstname,
        e.lastname,
        order = o
    };

说明:以employees左表,orders右表,orders 表中为空时,用null值填充。join的结果重命名ords,使用defaultifempty()函数对其再次查询。其最后的结果中有个order,因为from o in ords.defaultifempty() 是对ords组再一次遍历,所以,最后结果中的order并不是一个集合。但是,如果没有from o in ords.defaultifempty() 这句,最后的select语句写成select new { e.firstname, e.lastname, order = ords }的话,那么order就是一个集合。

4.投影的let赋值(projected let assignment):

说明:let语句是重命名。let位于第一个from和select语句之间。

这个例子从联接投影出最终“let”表达式:

var q =
    from c in db.customers
    join o in db.orders on c.customerid
    equals o.customerid into ords
    let z = c.city + c.country
    from o in ords
    select new
    {
        c.contactname,
        o.orderid,
        z
    };

5.组合键(composite key):

这个例子显示带有组合键的联接:

var q =
    from o in db.orders
    from p in db.products
    join d in db.orderdetails
        on new
        {
            o.orderid,
            p.productid
        } equals
            new
            {
                d.orderid,
                d.productid
            }
        into details
    from d in details
    select new
    {
        o.orderid,
        p.productid,
        d.unitprice
    };

说明:使用三个表,并且用匿名类来说明:使用三个表,并且用匿名类来表示它们之间的关系。它们之间的关系不能用一个键描述清楚,所以用匿名类,来表示组合键。还有一种是两个表之间是用组合键表示关系的,不需要使用匿名类。

6.可为null/不可为null的键关系(nullable/nonnullable key relationship):

这个实例显示如何构造一侧可为 null 而另一侧不可为 null 的联接:

var q =
    from o in db.orders
    join e in db.employees
        on o.employeeid equals
        (int?)e.employeeid into emps
    from e in emps
    select new
    {
        o.orderid,
        e.firstname
    };

order by操作

适用场景:对查询出的语句进行排序,比如按时间排序等等。

说明:按指定表达式对集合排序;延迟,:按指定表达式对集合排序;延迟,默认是升序,加上descending表示降序,对应的扩展方法是orderby和orderbydescending

1.简单形式

这个例子使用 orderby 按雇用日期对雇员进行排序:

var q =
    from e in db.employees
    orderby e.hiredate
    select e;

说明:默认为升序

2.带条件形式

注意:where和order by的顺序并不重要。而在t-sql中,where和order by有严格的位置限制。

var q =
    from o in db.orders
    where o.shipcity == "london"
    orderby o.freight
    select o;

语句描述:使用where和orderby按运费进行排序。

3.降序排序

var q = 
    from p in db.products
    orderby p.unitprice descending
    select p;

4.thenby

语句描述:使用复合的 orderby 对客户进行排序,进行排序:

var q =
    from c in db.customers
    orderby c.city, c.contactname
    select c;

说明:按多个表达式进行排序,例如先按city排序,当city相同时,按contactname排序。这一句用lambda表达式像这样写:

var q = 
    db.customers
    .orderby(c => c.city)
    .thenby(c => c.contactname).tolist();

在t-sql中没有thenby语句,其依然翻译为orderby,所以也可以用下面语句来表达:

var q = 
    db.customers
    .orderby(c => c.contactname)
    .orderby(c => c.city).tolist();

所要注意的是,多个orderby操作时,级连方式是按逆序。 对于降序的,用相应的降序操作符替换即可。

var q = 
    db.customers
    .orderbydescending(c => c.city)
    .thenbydescending(c => c.contactname).tolist();

需要说明的是,orderby操作,不支持按type排序,也不支持匿名类。比如

var q = 
    db.customers
    .orderby(c => new
    {
        c.city,
        c.contactname
    }).tolist();

会被抛出异常。错误是前面的操作有匿名类,再跟orderby时,比较的是类别。比如

var q = 
    db.customers
    .select(c => new
    {
        c.city,
        c.address
    })
    .orderby(c => c).tolist();

如果你想使用orderby(c => c),其前提条件是,前面步骤中,所产生的对象的类别必须为c#语言的基本类型。比如下句,这里city为string类型。

var q = 
    db.customers
    .select(c => c.city)
    .orderby(c => c).tolist();

5.thenbydescending

这两个扩展方式都是用在orderby/orderbydescending之后的,第一个thenby/thenbydescending扩展方法作为第二位排序依据,第二个thenby/thenbydescending则作为第三位排序依据,以此类推

var q =
    from o in db.orders
    where o.employeeid == 1
    orderby o.shipcountry, o.freight descending
    select o;

语句描述:使用orderby先按发往国家再按运费从高到低的顺序对 employeeid 1 的订单进行排序。

6.带groupby形式

var q = 
    from p in db.products
    group p by p.categoryid into g
    orderby g.key
    select new {
        g.key,
        mostexpensiveproducts =
            from p2 in g
            where p2.unitprice == g.max(p3 => p3.unitprice)
            select p2
    };

语句描述:使用orderby、max 和 group by 得出每种类别中单价最高的产品,并按 categoryid 对这组产品进行排序。