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

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返回值作为字段值。如图所示:

生成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 对这组产品进行排序。

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐