C# 如果记录存在,则使用内部联接,否则使用左联接
我有以下表格结构: dbo.所有者C# 如果记录存在,则使用内部联接,否则使用左联接,c#,sql-server,entity-framework,C#,Sql Server,Entity Framework,我有以下表格结构: dbo.所有者 OwnerID OwnerName 1 John 2 Marie 3 Alex 和dbo.Pet PetID PetTag Status OwnerID 1 A341 Active 1 2 A342 Inactive 1 3 A343 Active 2 4 A345 Active 2 我需要归还所有只养宠物或没有宠
OwnerID OwnerName
1 John
2 Marie
3 Alex
和dbo.Pet
PetID PetTag Status OwnerID
1 A341 Active 1
2 A342 Inactive 1
3 A343 Active 2
4 A345 Active 2
我需要归还所有只养宠物或没有宠物的主人
因此,在上面的示例中,我需要返回所有者2(所有宠物都处于活动状态)和所有者3(没有宠物)
我将使用实体框架在C#中提取数据,但简单的SQL就足够了
以下是我到目前为止的想法:
select mi.* from Owner o
join Pet p
on o.OwnerID= p.OwnerID
where o.Status='Active'
union select * from Owner
where OwnerID not in (select OwnerID from Pet)
现在,上面的查询可以工作,但它包括OwnerID=1。我还想知道是否有一种方法可以在不使用联合的情况下在1个查询中实现这一点。试试:
select DISTINCT
o.Id
FROM Owner o
LEFT JOIN Pet p ON o.OwnerID= p.OwnerID
where p.Status='Active' OR p.OwnerID IS NULL
这个简单的查询就可以了
OwnerId OwnerName
2 Marie
3 Alex
如果您想选择至少有一只宠物处于活动状态或没有宠物的主人,请使用下面的查询
SELECT o.OwnerID o.OwnerName
FROM Owner o
LEFT JOIN Pet p
ON o.OwnerID= p.OwnerID
AND (p.Status='Active'
OR p.OwnerID is NULL)
OwnerId OwnerName
1 John
2 Marie
3 Alex
此查询将返回所有者名称,直到该所有者的所有宠物处于非活动状态
现在来看另一个例子
如果您的表有可能在Pets表中将OwnerId设置为NULL。
请使用下面的查询。(Mysql)
在子查询中添加了IFNULL()
如果
状态的唯一值是“活动”和“非活动”,实际上可以简化查询。当你说:
我需要归还所有只养宠物或没有宠物的主人
这实际上可以转化为:
我需要归还所有没有闲置宠物的主人
然后您的查询就变得容易多了
在实体框架查询中:
owners = context.Owners
.Where(o => !o.Pets.Any(p => p.Status == "Inactive"))
.ToList();
由此生成的SQL查询是:
SELECT
[Extent1].[OwnerID] AS [OwnerID],
[Extent1].[OwnerName] AS [OwnerName]
FROM [dbo].[Owners] AS [Extent1]
WHERE NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Pets] AS [Extent2]
WHERE ([Extent1].[OwnerID] = [Extent2].[OwnerID]) AND (N'Inactive' = [Extent2].[Status])
)
或者,为了消除混乱:
SELECT
OwnerID,
OwnerName
FROM Owners o
WHERE NOT EXISTS (SELECT
1
FROM Pets p
WHERE (o.OwnerID = p.OwnerID AND p.Status = 'Inactive')
)
如果状态有更多值,可以使用(实体框架):
这将生成SQL查询:
SELECT
[Extent1].[OwnerID] AS [OwnerID],
[Extent1].[OwnerName] AS [OwnerName]
FROM [dbo].[Owners] AS [Extent1]
WHERE (( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Pets] AS [Extent2]
WHERE ([Extent1].[OwnerID] = [Extent2].[OwnerID]) AND (N'Active' = [Extent2].[Status])
)) OR ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Pets] AS [Extent3]
WHERE [Extent1].[OwnerID] = [Extent3].[OwnerID]
))) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Pets] AS [Extent4]
WHERE ([Extent1].[OwnerID] = [Extent4].[OwnerID]) AND (N'Inactive' = [Extent4].[Status])
))
您可能想测试它的性能,可能有更好的方法,但它会给出期望的结果。但它假定您具有外键/导航属性
SELECT DISTINCT RESULT FROM (
SELECT CASE WHEN POID is NULL
THEN OID
WHEN OID NOT IN (SELECT DISTINCT
OwnerID from Pet
WHERE Status='Inactive')
THEN OID
END AS RESULT
FROM (
SELECT O.OwnerID as OID, P.OwnerID as POID
FROM Owner o
LEFT JOIN Pet p
ON o.OwnerID= p.OwnerID
) T
)T2 WHERE RESULT IS NOT NULL
有趣的是,尽管您将其标记为实体框架,但大多数答案并没有给出实体框架为您提供的简化
所有者
和宠物
之间存在一对多关系。每个所有者
拥有零只或多只宠物
,每个宠物
只属于一个所有者
如果您已为a正确配置了实体框架类,则它们将如下所示:
class Owner
{
public int Id {get; set;}
// every Owner has zero or more Pets:
public virtual ICollection<Pet> Pets {get; set;}
... // other properties
}
class Pet
{
public int Id {get; set;}
// every Pet belongs to exactly one Owner, using foreign key:
public int OwnerId {get; set;}
public Owner Owner {get; set;}
}
class MyDbConnection : DbConnection
{
public DbSet<Owner> Owners {get; set;}
public DbSet<Pet> Pets {get; set;}
}
实体框架将认识到这需要一个连接,并将其转换为正确的内部连接
第二个查询更简单,可能更快,因为只要找到一个不活动的Pet
,您就可以继续查找下一个Owner
var ownersWithoutInactivePets = dbContext.Owners // give me all Owners
.Where(owner => !owner.Pets // that don't have
.Any(pet => pet.Status == Inactive); // any inactive Pets
同样,entity framework将为您执行连接有趣的是,使用左连接也可以执行此操作。我不知道这是否与其他答案建议的NOT EXISTs查询的性能有所不同
CREATE TABLE [Owner] (
OwnerID int PRIMARY KEY,
OwnerName nvarchar(50)
);
INSERT INTO [Owner]
VALUES
(1, 'John'),
(2, 'Marie'),
(3, 'Alex');
CREATE TABLE Pet (
PetID int PRIMARY KEY,
PetTag nvarchar(10),
Status nvarchar(30),
OwnerID int FOREIGN KEY REFERENCES [Owner](OwnerID)
);
INSERT INTO Pet
VALUES
(1,'A341','Active', 1),
(2,'A342','Inactive', 1),
(3,'A343','Active', 2),
(4,'A345','Active', 2);
SELECT * FROM [Owner];
SELECT * FROM Pet;
SELECT
o.*
FROM
[Owner] o
LEFT JOIN Pet p
ON o.OwnerID = p.OwnerID
AND p.Status <> 'Active'
WHERE
p.OwnerID IS NULL;
DROP TABLE Pet, [Owner];
创建表[所有者](
OwnerID int主键,
业主名称nvarchar(50)
);
插入[所有者]
价值观
(1,'约翰'),
(2,'玛丽'),
(3,“亚历克斯”);
创建桌面宠物(
PetID int主键,
PetTag nvarchar(10),
nvarchar状态(30),
OwnerID int外键引用[Owner](OwnerID)
);
插入宠物
价值观
(1,'A341','Active',1),
(2,'A342','Inactive',1),
(3,'A343','Active',2),
(4,'A345','Active',2);
从[所有者]中选择*;
从宠物中选择*;
挑选
o*
从…起
[所有者]o
左连接Pet p
在o.OwnerID=p.OwnerID上
和p.状态“活动”
哪里
p、 OwnerID为空;
投桌宠物,[主人];
听一听你简单的英语提问:我需要返回所有只养宠物或没有宠物的主人。你需要一个由那些有活跃宠物和没有宠物的人组成的简单联盟。谢谢Andrei,这应该行得通。但是如果有更多的状态呢?我可以将p.Status='Inactive'更改为p.Status'Active'?如果一个人同时拥有一只活动和一只非活动宠物,会发生什么情况。。我认为您的查询在这个条件下不起作用。两个查询都会忽略这些所有者,这就是问题所指定的。除非我误解了你?一般来说,如果可以的话,避免不在中。如果Pet.OwnerID
列中有空值(即没有主人的宠物),您的查询将变成南瓜。不存在
版本的@AndreiOdegov不易受此影响。请从OwnerID不在的Owner中选择不同的OwnerName(从状态为“Inactive”的Pet中选择IFNULL(OwnerID,0);我将IFNULL添加到select in NOT in中,它正在工作。另外,由于Owner在OwnerId上是唯一的,因此不需要DISTINCT,它可能会执行不必要的唯一排序。我不会删除该ID,因为多个人可以有相同的名称。删除的是不同的,添加的是不同的id@FarazPV,在MS SQL Server中没有ifnull()
函数。另外,您是否将解决方案的执行计划与不存在的执行计划进行了比较?我打赌你用这样的包装会失去吸引力。不正确。这也将返回id为1的所有者。需要归还那些只有活动宠物的主人。
SELECT DISTINCT RESULT FROM (
SELECT CASE WHEN POID is NULL
THEN OID
WHEN OID NOT IN (SELECT DISTINCT
OwnerID from Pet
WHERE Status='Inactive')
THEN OID
END AS RESULT
FROM (
SELECT O.OwnerID as OID, P.OwnerID as POID
FROM Owner o
LEFT JOIN Pet p
ON o.OwnerID= p.OwnerID
) T
)T2 WHERE RESULT IS NOT NULL
class Owner
{
public int Id {get; set;}
// every Owner has zero or more Pets:
public virtual ICollection<Pet> Pets {get; set;}
... // other properties
}
class Pet
{
public int Id {get; set;}
// every Pet belongs to exactly one Owner, using foreign key:
public int OwnerId {get; set;}
public Owner Owner {get; set;}
}
class MyDbConnection : DbConnection
{
public DbSet<Owner> Owners {get; set;}
public DbSet<Pet> Pets {get; set;}
}
using (var dbConnection = new MyDbConnection())
{
var requestedOwners = dbConnection.Owners // Give me all Owners
.Where(owner => !owner.Pets.Any() // that have no Pets at all
|| owner.Pets.All(pet => pet.Status == Active)); // or have only active Pets
}
var ownersWithoutInactivePets = dbContext.Owners // give me all Owners
.Where(owner => !owner.Pets // that don't have
.Any(pet => pet.Status == Inactive); // any inactive Pets
CREATE TABLE [Owner] (
OwnerID int PRIMARY KEY,
OwnerName nvarchar(50)
);
INSERT INTO [Owner]
VALUES
(1, 'John'),
(2, 'Marie'),
(3, 'Alex');
CREATE TABLE Pet (
PetID int PRIMARY KEY,
PetTag nvarchar(10),
Status nvarchar(30),
OwnerID int FOREIGN KEY REFERENCES [Owner](OwnerID)
);
INSERT INTO Pet
VALUES
(1,'A341','Active', 1),
(2,'A342','Inactive', 1),
(3,'A343','Active', 2),
(4,'A345','Active', 2);
SELECT * FROM [Owner];
SELECT * FROM Pet;
SELECT
o.*
FROM
[Owner] o
LEFT JOIN Pet p
ON o.OwnerID = p.OwnerID
AND p.Status <> 'Active'
WHERE
p.OwnerID IS NULL;
DROP TABLE Pet, [Owner];