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)

效果
CROSS APPLY的应用例子_第1张图片

应用例子3

有一张学生表,分别name,学科,分数 这三个字段,如下:
CROSS APPLY的应用例子_第2张图片
我要看语文第一名,数学前两名,英语前三名的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

CROSS APPLY的应用例子_第3张图片

你可能感兴趣的:(SQL)