SQL SELECT在连接中查找类似子字符串的名称

时间:2016-10-01 17:36:53

标签: mysql sql sql-server tsql

enter image description here

我该如何解决这个问题?

enter image description here

我的SQL语句是:

select ename 
from employee e 
    inner join certified c on e.eid = c.eid 
    inner join aircraft a on c.aid = a.aid 
    where cruisingrange> 1000 && a.aname not like'%b'

我对此查询的回答是 Jacob和Emily 这是错误的。不应该检索雅各布。

我应该如何修改或添加SQL语句?

脚本:

CREATE TABLE Employee (
    Eid int, 
    Ename nvarchar(100), 
    Salary int
)

INSERT INTO Employee VALUES
(1,'Jacob',85000),(2,'Michael',55000),(3,'Emily',80000),
(4,'Ashley',110000),(5,'Daniel',80000),(6,'Olivia',70000)

CREATE TABLE Aircraft (
    Aid int, 
    Aname nvarchar(100), 
    Cruisingrange int
)

INSERT INTO Aircraft VALUES
(1,'a1',800),(2,'a2b',700),(3,'a3',1000),
(4,'a4b',1100),(5,'a5',1200)

CREATE TABLE Flight (
    Flno int, 
    Fly_from nvarchar(100), 
    Fly_to nvarchar(100), 
    Distance int,
    Price int
)

INSERT INTO Flight VALUES
(1,'LA','SF',600,65000),(2,'LA','SF',700,70000),(3,'LA','SF',800,90000),
(4,'LA','NY',1000,85000),(5,'NY','LA',1100,95000)

CREATE TABLE Certified (
    Eid int, 
    Aid int, 
    CertDate date
)

INSERT INTO Certified VALUES
(1, 1, '2005-01-01'),(1, 2, '2001-01-01'),(1, 3, '2000-01-01'),
(1, 5, '2000-01-01'),(2, 3, '2002-01-01'),(2, 2, '2003-01-01'),
(3, 3, '2003-01-01'),(3, 5, '2004-01-01')

2 个答案:

答案 0 :(得分:1)

我会读“未通过认证”来检查是否存在行。

如果存在匹配的行,则不要返回该员工。如果匹配的行不存在,则仅返回emplouee。

您如何找到匹配的行,以确定员工是否“获得任何认证”?

有几种方法。使用的两种最佳方法1)反连接和2)NOT EXISTS(相关子查询)。

示例 NOT EXISTS (correlated subquery)

在这两种方法中,更容易看出它是如何工作的。

  FROM e
 WHERE NOT EXISTS ( SELECT 1
                      FROM certified c
                      JOIN aircraft a 
                        ON a.id = c.a_id
                     WHERE a.aname LIKE '%b%'   
                       AND c.e_id = e.id
                  ) 

请注意子查询的谓词中对外部表(e.id)的引用。子查询与外部查询“相关”。

想一想这样:对于外部查询返回的每一行,执行子查询,传递e.id的值。 (优化器不 以这种方式执行操作;这只是一种思考我们所要求的简单方法。)

如果子查询返回1行或更多行,则满足条件EXISTS,并返回TRUE。如果子查询返回零行,则EXISTS的计算结果为FALSE。

反连接模式

的示例

这种方法可能需要一点点才能让你的大脑感染。一旦你“搞定”它,它就是一个非常有用的工具,可以方便地使用SQL工具带。

如果我们使用OUTER JOIN,并从e拉回所有行以及任何匹配的行,那么我们可以“排除”找到匹配的行。

  FROM e
  LEFT
  JOIN ( SELECT c.e_id
           FROM certified c
           JOIN aircraft a
             ON a.id = c.a_id
          WHERE a.aname LIKE '%b%'
         GROUP BY c.e_id
       ) b
    ON b.e_id = e.id
 WHERE b.e_id IS NULL

内联视图查询具体化为名为b的派生表。该查询旨在返回经认证可飞行符合指定标准的任何飞机的每位员工的身份证。然后,派生表中的行外部连接到e

“技巧”是外部联接(包括具有匹配的行,没有匹配的行,以及WHERE子句中排除具有匹配的行的条件。

我希望其他人提供一个如何使用NOT IN (subquery)的示例。使用该方法,请注意子查询返回任何NULL值时会发生什么。 (提示:您需要确保子查询永远不会返回NULL。)

这仅表明满足“未经任何认证”标准的几种可能方法中的两种。

显然,需要添加额外的连接/子查询来评估查询中的其他条件。

答案 1 :(得分:0)

您可以在 SQL Server 中使用此查询(也许它也适用于MySQL):

SELECT e.Ename
FROM Employee e 
INNER JOIN Certified c 
    ON e.Eid = c.Eid 
LEFT JOIN aircraft a 
    ON c.Aid = a.Aid and a.Cruisingrange> 1000
LEFT JOIN aircraft a1 
    ON c.Aid = a1.Aid and a1.aname like'%b%'
GROUP BY e.Ename
HAVING MAX(a.Aid) IS NOT NULL AND MAX(a1.Aid) IS NULL

我们根据需要加入所有需要的表格。然后我们按Ename进行分组并使用MAX(a.Aid) IS NOT NULL,这意味着飞行员可以使用Cruisingrange > 1000MAX(a1.Aid) IS NULL操作飞行器,这意味着他没有在{{1}的任何飞机上获得认证在名字中。