我们举例来说明:
假设有三张表,用户表,订单表,以及用户订单关联表。
用户表有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;
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
通过传入一个十进制的数字,分别返回对应的二进制、八进制、十六进制
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)
有一张学生表,分别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