SQL Server 交叉应用不起作用?

Posted

技术标签:

【中文标题】SQL Server 交叉应用不起作用?【英文标题】:SQL Server cross apply not working? 【发布时间】:2013-12-16 14:12:13 【问题描述】:

http://sqlfiddle.com/#!3/78273/1

create table emptb1
(
id int,
name varchar(20),
dept int
)

insert into emptb1 values (1,'vish',10);
insert into emptb1 values (2,'vish',10);
insert into emptb1 values (3,'vish',30);
insert into emptb1 values (4,'vish',20);

create table depttb1
(
id int,
name varchar(20)
)

insert into depttb1 values(10,'IT')
insert into depttb1 values(20,'AC')
insert into depttb1 values(30,'LIC')

select * from emptb1

select e.id, e.name, a.id
from emptb1 e
cross apply
(
select top 1 * from depttb1 d
where d.id = e.dept
order by d.id desc
) a

我正在尝试学习交叉应用,因为它类似于内部连接,但适用于函数。

在上面的查询中,我假设它应该只需要 dept=30,因为 order d.id desc 只会给出前 1st id,即 30,然后它应该返回 dept id = 30 的员工,但它给了我所有的行和所有部门。

查询出了什么问题,或者我对交叉应用概念的解释有误。

【问题讨论】:

您查询的实际上是“对于每个员工,找到他们工作的所有部门,然后选择该员工工作的部门 ID 最高的部门” - 现在,显然,使用您的架构,每个员工只为一个部门工作,但仍然会为每个员工返回一行。 @Damien_The_Unbeliever 好的,如果员工为 40 和 50 等两个不同的部门工作,它只会给我 50。但我尝试插入一个具有 40 和 50 的部门的员工,但它仍然给了我两个部门. 那是因为在当前架构中对在两个部门中拥有员工进行建模的唯一方法是在 emptbl 中有两行(可能)具有相同的 idname?如果是这样,请再次查看您的查询 - 它在 cross apply 期间没有说明这些列 - 所以首先它对 deptid 为 40 的行执行此操作,depttbl 中唯一匹配的行是带有 @ 的行987654329@ 40。所以这是emptbl 中第一行的前 1 个匹配行。然后,我们对deptid为50的那一行做同样的处理。这一次,我们匹配id为50的那一行,又是前1... 【参考方案1】:

你说“在上面的查询中,我假设它应该只需要 dept=30,因为 order d.id desc 只会给出前 1st id,即 30,然后它应该返回 dept id = 30 的员工”。

这不是它的工作原理。这是您的查询(为了清楚起见,重新格式化了一点):

select e.id, e.name, a.id
from   emptb1 e
cross apply
(
    select top 1 * 
    from depttb1 d
    where d.id = e.dept
    order by d.id desc
) a

APPLY 关键字意味着内部查询(逻辑上)为外部查询的每一行调用一次。对于内部查询中发生的事情,了解 SELECT 的子句执行的逻辑顺序会很有帮助。这个顺序是:

    FROM 子句 WHERE 子句 SELECTORDER BY 子句 TOP运营商

请注意,在您的内部查询中,TOP 运算符在WHERE 子句之后被应用最后。这意味着where d.id = e.dept 将首先将内部行减少到d.id 与外部行的e.dept 匹配的那些(不一定是30),然后对它们进行排序,然后返回第一个。它对外部查询中的每一行执行此操作。很明显,他们中的许多人不会是30

你想要做的会更类似于这个(仍然保留CROSS APPLY):

select e.id, e.name, a.id
from   emptb1 e
cross apply
(
    select top 1 * 
    from
    (
        select top 1 * 
        from depttb1 d
        order by d.id desc
    ) b
    where b.id = e.dept
) a

在这里,逻辑已通过使用另一个嵌套子查询进行了重新排序,该子查询确保 ORDER BY,然后 TOP 1WHERE 子句之前得到应用。 (请注意,这通常不是推荐的方法,因为嵌套子查询会妨碍可读性,我只是在这里使用它来保留 CROSS APPLY 并保留原始结构的其余部分)。

【讨论】:

噢!我认为我从这个答案中获得了我的秘密帽子!还是不知道为什么……?【参考方案2】:

为了扩展 Damien 的评论,内部查询:

select top 1 * from depttb1 d
where d.id = e.dept
order by d.id desc

将为外部查询中的每一行运行:

select e.id, e.name, a.id
from emptb1 e

因此,您将始终从每一行的内部查询中获得匹配项。我认为您希望内部查询只运行一次,但这不是 APPLY 所做的。

因此,从您的外部查询中取出第一行,ID 为 1,部门 ID 为 10,您的内部查询将转换为:

select top 1 * from depttb1 d
where d.id = 10  //this is the dept id for the current row from your outer query
order by d.id desc

【讨论】:

那么top 1的用户是什么* depttb 中的每个值只有 1 行,我不明白你想用 TOP 1 做什么。【参考方案3】:

要在不使用交叉应用的情况下解决此问题,请使用子查询。但是在您的示例中,它只会返回一行,假设 id 值正在增加,则输入的最后一个部门。

-- Using a sub query to find max dept
select e.id, e.name
from emptb1 e
where e.dept in
(
select top 1 id 
from depttb1 
order by id desc
)

CROSS APPLY 背后的想法有点像 CROSS JOIN。这将返回所有行。 DBA 将它与许多动态管理视图 (DMV) 一起使用,这些动态管理视图 (DMV) 是表值函数 (TVF)

你想要的是 OUTER APPLY 有点像 LEFT JOIN。

select e.id, e.name
from emptb1 e
outer apply 
    (
    select top 1 d.id from depttb1 d order by d.id desc
    ) AS m (id)
where e.dept = m.id

查看我关于这些概念的文章。

交叉申请 - http://craftydba.com/?p=3767

外部申请 - http://craftydba.com/?p=3796

表值函数(内联)-http://craftydba.com/?p=3733

表值函数(多行)-http://craftydba.com/?p=3754

【讨论】:

CROSS APPLYOUTER APPLY 在这种情况下返回相同的结果,不是吗? OUTER APPLY 将返回来自“外”表的行,即使“内”表没有匹配项。 是的 - 在这个玩具示例中,CROSS = OUTER。但是,如果我们有更有趣的东西,比如每个部门的每个订单的总销售额,并且我们想要前 3 个,如果有数据,或者如果没有,则为空条目,外部应用就是要走的路。查看 Rob Farley 的文章。 sqlblog.com/blogs/rob_farley/archive/2011/04/13/…

以上是关于SQL Server 交叉应用不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

触发器在 SQL Server 的 BulkCopy 中不起作用

SQL Server 可用性组自动故障转移不起作用

OPENJSON 在 SQL Server 中不起作用?

SQL Server 数据库中带有 Group by 子句的 JPQL 不起作用

sql-server 堆栈查询 sql 注入更新查询不起作用

SQL Server 2008 - CAST 到 nvarchar 不起作用