CROSS APPLY的应用例子

CROSS APPLY

我们举例来说明:
假设有三张表,用户表,订单表,以及用户订单关联表。
用户表有10000数据,订单表有3650000数据,用户订单关联表记录了每个用户有哪些订单

CREATE TABLE dbo.DimUser (
	UserID INT IDENTITY(1, 1)
	,UserName NVARCHAR(200)
)

CREATE TABLE dbo.FctOrderHeader (
	OrderID BIGINT IDENTITY(1, 1)
	,OrderDate DATETIME
	,OrderAmount BIGINT
)

CREATE TABLE dbo.FctOrderUser (
	OrderID BIGINT
	,UserID INT
)

需求是:查询每个用户的最近三张订单

第一步:我们需要新建一个表函数,用来接收用户作为参数,返回用户对应订单的详细信息。

CREATE FUNCTION dbo.GetUserOrder (@UserID INT)
RETURNS TABLE
AS
RETURN (
	SELECT ord.OrderID, ord.OrderDate, ord.OrderAmount
	FROM FctOrderHeader ord
	INNER JOIN FctOrderUser usr ON ord.OrderID = usr.OrderID
	AND usr.UserID = @UserID
)

第二步:最精彩之处在于,我们可以用第一张表里的字段作为限制条件,来将第二张表里的数据筛选出来,并且进行计算,合成到第一张表里,而 Inner join 的第二张表,即 right table 是独立的,并不能接收第一张表的任何字段:

SELECT usr.UserName, ord.OrderID, ord.OrderDate, ord.OrderAmount
FROM DimUser usr
CROSS APPLY (
	SELECT TOP 3 OrderID, OrderDate, OrderAmount
	FROM dbo.GetUserOrder(usr.UserID)
	ORDER BY OrderDate DESC, OrderAmount DESC
) ord
ORDER BY usr.UserName ASC;

Inner join 也可以实现类似的查询,但是性能上会差很多,我测出来是 4s:12s 的区别:

WITH BASE_QUERY
AS (
	SELECT ord.OrderID, ord.OrderDate, ord.OrderAmount, usr.UserID, 
	Row_Number() OVER (PARTITION BY usr.UserID ORDER BY ord.OrderDate DESC, ord.OrderAmount DESC) AS RNK
FROM FctOrderHeader ord
INNER JOIN FctOrderUser usr ON ord.OrderID = usr.OrderID
)

SELECT TOP 10 usr.UserName,ord.OrderID,ord.OrderDate,ord.OrderAmount
FROM DimUser usr
INNER JOIN BASE_QUERY ord ON usr.UserID = ord.UserID
WHERE ord.RNK <= 3
ORDER BY usr.UserName

应用例子2:

通过传入一个十进制的数字,分别返回对应的二进制、八进制、十六进制

ALTER FUNCTION fnConversion
(
    @NUM INT
)
RETURNS
@t_table TABLE
(
    [Binary] varchar(64),
    Octal varchar(16),
    Hexadecimal varchar(8)
)
AS
BEGIN
    DECLARE @RESULT2 VARCHAR(500)='',@RESULT8 VARCHAR(500)='',@RESULT16 VARCHAR(500)=''; 
    WITH CTE AS( 
        SELECT @NUM/2 D2,@NUM%2 S2,@NUM/8 D8,@NUM%8 S8,@NUM/16 D16,@NUM%16 S16,1 [INDEX] 
        UNION ALL 
        SELECT D2/2 , D2%2,D8/8 , D8%8,D16/16 , D16%16,[INDEX]+1 FROM CTE WHERE D2>0 
    ) 
    SELECT @RESULT2+=CAST(S2 AS VARCHAR(1))
          ,@RESULT8+=CASE WHEN D8=0 AND S8=0 THEN '' ELSE CAST(S8 AS VARCHAR(1)) END
          ,@RESULT16+=CASE WHEN D16=0 AND S16=0 THEN ''
                           ELSE CASE CAST(S16 AS VARCHAR(5))
                                WHEN '10' THEN 'A' 
                                WHEN '11' THEN 'B' 
                                WHEN '12' THEN 'C' 
                                WHEN '13' THEN 'D' 
                                WHEN '14' THEN 'E' 
                                WHEN '15' THEN 'F' 
                                ELSE CAST(S16 AS VARCHAR(5))
                            END
                        END
    FROM CTE ORDER BY [INDEX] DESC
    INSERT INTO @t_table
    SELECT @RESULT2,@RESULT8,@RESULT16
    RETURN 
END
Select * From fnConversion(255)

在这里插入图片描述
应用
有表如下,只包含一列NUM
在这里插入图片描述
使用CROSS APPLY

SELECT * FROM #T a CROSS APPLY [dbo].[fnConversion](a.num)

效果
在这里插入图片描述

应用例子3

有一张学生表,分别name,学科,分数 这三个字段,如下:
在这里插入图片描述
我要看语文第一名,数学前两名,英语前三名的name,学科,分数,用cross apply实现方法如下:

SELECT b.* FROM (
select Subject='Chiness',num=1 union all
select 'Math',2 union all
select 'English',3
)a cross apply (select top(a.num) * from Students where Subject=a.Subject )b

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值