从外部查询访问派生表

时间:2018-11-05 02:11:55

标签: sql sql-server

在以下问题中 Filtering based on Joining Multiple Tables in SQL

我设法确定发帖人问题是因为他正在从外部查询访问派生表。

我不明白为什么会这样。

因此,如果您运行以下内容

create table salesperson (
    id int, name varchar(40)
)
create table customer (
    id int, name varchar(40)
)
create table orders (
    number int, cust_id int, salesperson_id int
)
insert into salesperson values (1, 'abe'); insert into salesperson values (2, 'bob');
insert into salesperson values (5, 'chris'); insert into salesperson values (7, 'dan');
insert into salesperson values (8, 'ken'); insert into salesperson values (11, 'joe'); 
insert into customer values (4, 'Samsonic'); insert into customer values (6, 'panasung');
insert into customer values (7, 'samony'); insert into customer values (9, 'orange');
insert into orders values (10, 4, 2); insert into orders values (20, 4, 8);
insert into orders values (30, 9, 1); insert into orders values (40, 7, 2);
insert into orders values (50, 6, 7); insert into orders values (60, 6, 7);
insert into orders values (70, 9, 7);  

SELECT *
FROM salesperson s 
INNER JOIN orders o ON s.id = o.salesperson_id
INNER JOIN customer c ON o.cust_id = c.id
WHERE s.name NOT IN (
    select s.name where c.name='Samsonic'
)

SELECT *
FROM salesperson s 
INNER JOIN orders o ON s.id = o.salesperson_id
INNER JOIN customer c ON o.cust_id = c.id
WHERE s.name NOT IN (
    SELECT s.name
    FROM  salesperson s 
    INNER JOIN orders o ON s.id = o.salesperson_id
    INNER JOIN customer c ON o.cust_id = c.id
    WHERE c.name = 'Samsonic'
)

第一个select语句访问外部查询中的派生表,而另一个则创建自己的联接并派生自己的表。

为什么第一个选择包含bob而另一个则不包含bob?

3 个答案:

答案 0 :(得分:1)

在您的第一个查询中,您仅删除客户名称为Samsonic的行,因为Bob记录了一个输出的谐音记录。

在第二个中,您得到了一个具有客户名称Samsonic的销售人员,在这种情况下,您同时获得了Bob和Ken,然后您使用“ not in”删除了Bob和Ken的所有记录,因此鲍勃被移走了,所以你什么也没得到。

答案 1 :(得分:1)

区别在于,在您的第一个查询中,您仅删除涉及Samsonic的订单,因为排除项仅查看当前行中的数据。鉴于它的声音,您想删除曾经销售过Samsonic的任何销售人员。您可以在以下查询的结果中看到与的区别:

SELECT *, s.name, c.name
  , case when s.name NOT IN (
    select s.name where c.name='Samsonic'
) then 1 else 0 end /* Order not Samsonic */
  , case when not exists (
    select 1
    from Orders O1
    inner join Customer C1 on o1.cust_id = c1.id
    where C1.Name = 'Samsonic' and o1.salesperson_id = O.salesperson_id
  ) then 1 else 0 end /* Salesperson never sold a Samsonic */
FROM salesperson s 
INNER JOIN orders o ON s.id = o.salesperson_id
INNER JOIN customer c ON o.cust_id = c.id

答案 2 :(得分:1)

您的第一个查询的select中没有from子句。因此where等效于:

WHERE s.name NOT IN (CASE WHEN c.name = 'Samsonic' THEN s.name END)

或更简单地说:

WHERE c.name <> 'Samsonic'

Bob的订单不与'Samsonic'匹配,因此Bob在结果集中。换句话说,逻辑是分别查看每一行

第二个版本正在查看已下订单的所有名称。鲍勃是这些名字之一,因此这适用于鲍勃发出的所有订单。

如果要排除曾经向'Samsonic'下订单的所有销售人员,那么我建议使用窗口函数而不是复杂的逻辑:

SELECT *
FROM (SELECT s.id as salesperson_id, s.name as salesperson_name, c.id as customer_id, c.name as customer_name, o.number,
             SUM(CASE WHEN c.name = 'Samsonic' THEN 1 ELSE 0 END) OVER (PARTITION BY s.id) as num_samsonic
      FROM salesperson s INNER JOIN
           orders o 
           ON s.id = o.salesperson_id INNER JOIN
           customer c
           ON o.cust_id = c.id
      WHERE c.name <> 'Samsonic'
     ) soc
WHERE num_samsonic = 0