如果记录存在则使用内连接,否则使用左连接

时间:2017-11-03 03:32:35

标签: c# sql-server entity-framework

我有以下表结构:

dbo.Owner

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(无宠物)

我将使用Entity Framework在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个查询中执行此操作。

7 个答案:

答案 0 :(得分:5)

如果Status的唯一值为“有效”和“无效”,则实际上可以简化查询。当你说:

  

我需要归还所有只有活跃宠物或没有宠物的主人。

然后实际转换为:

  

我需要归还所有没有非活动宠物的主人。

然后您的查询变得更加容易。

在实体框架查询中:

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')
    )

如果您有更多状态值,则可以使用(实体框架):

owners = context.Owners
    .Where(o => o.Pets.Any(p => p.Status == "Active") || !o.Pets.Any())
    .Where(o => !o.Pets.Any(p => p.Status == "Inactive" /* || p.Status == "Lost" and any other values */))
    .ToList();

将生成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])
    ))

您需要对性能进行测试,并且可能有更好的方法,但它会提供所需的结果。它确实假设你有外键/导航属性。

答案 1 :(得分:4)

尝试the following query

select o.*
from dbo.owner o
where not exists(
  select *
  from dbo.pet p
  where p.ownerid=o.ownerid and
    p.status='Inactive'
);

答案 2 :(得分:3)

SELECT  OwnerID, OwnerName 
FROM Owner 
WHERE OwnerID NOT IN (
SELECT OwnerID from Pet
WHERE Status='Inactive'

这个简单的查询可以做到这一点。

OwnerId        OwnerName 
2              Marie
3              Alex

如果您想选择至少一个ACTIVE或NO PET的所有者,请使用以下查询。

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

此查询将返回OWNER名称,直到该所有者的所有宠物都是非活动

现在换另一个案例..

如果您的表有可能在Pets Table中将OwnerId设为NULL。 请使用以下查询。 (MySQL的)

SELECT OwnerID, OwnerName 
FROM Owner 
WHERE OwnerID NOT IN (
   SELECT IFNULL(OwnerID,0) from Pet
   WHERE Status='Inactive');

在子查询中添加了IFNULL()。

SQLFIDDLE

答案 3 :(得分:2)

有趣的是,可以通过LEFT JOIN执行此操作。我不知道这是否与其他答案建议的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];

答案 4 :(得分: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

答案 5 :(得分: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

SQL Fiddle

答案 6 :(得分:0)

有趣的是,尽管您将其标记为实体框架,但大多数答案都没有提供实体框架为您提供的简化。

OwnersPets之间存在一对多的关系。每个Owner都有零个或多个Pets,每个Pet只属于一个Owner

如果您已为one-to-many relationship正确配置了实体框架类,则它们将类似于:

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;}
}

这足以让实体框架认识到您设计了一对多关系。只要需要,实体框架就会为您做正确的连接。

  

我需要归还所有只有活跃宠物或没有宠物的主人。

这与以下相同:

  

我需要归还所有没有非活动宠物的主人

(自我注意:没有宠物的主人,肯定没有非活动宠物!)

如果您正确设置了类,则查询将更具可读性。您可以考虑集合,而不是使用ID

相互关联的表
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
}

实体框架将认识到这需要一个连接,并将其转换为正确的内连接。

第二个查询更简单,可能更快,因为只要找到非活动Owner,您就可以继续下一个Pet

var ownersWithoutInactivePets = dbContext.Owners   // give me all Owners
    .Where(owner => !owner.Pets                    // that don't have
        .Any(pet => pet.Status == Inactive);       // any inactive Pets

同样,实体框架将为您进行连接