INRE JOIN
och KORSA APPLY
(samma med LEFT JOIN
och YTTRE APPLY
) är mycket nära besläktade. I ditt exempel skulle jag anta att motorn kommer att hitta samma utförandeplan.
- En
JOIN
är en länk mellan två uppsättningar över ett villkor - en
APPLY
är en radvis underanrop
Men - som nämnts ovan - optimeraren är väldigt smart och kommer - åtminstone i så lätta fall - att förstå att det handlar om samma sak.
JOIN
kommer att försöka samla in underuppsättningen och länka den över det angivna villkoret-
APPLY kommer att försöka ropa upp det relaterade resultatet med den aktuella radens värden om och om igen.
Det finns skillnader i att anropa tabellvärderade-funktioner (bör vara inline -syntax!), med XML-metoden .nodes()
och med mer komplexa scenarier.
Ett exempel på hur man kan använda APPLY
för att simulera variabler
...för att använda resultatet av en radvis beräkning som om du skulle använda en variabel:
DECLARE @dummy TABLE(ID INT IDENTITY, SomeString VARCHAR(100));
INSERT INTO @dummy VALUES('Want to split/this at the two/slashes.'),('And/this/also');
SELECT d.ID
,d.SomeString
,pos1
,pos2
,LEFT(d.SomeString,pos1-1)
,SUBSTRING(d.SomeString,pos1+1,pos2-pos1-1)
,SUBSTRING(d.SomeString,pos2+1,1000)
FROM @dummy AS d
CROSS APPLY(SELECT CHARINDEX('/',d.SomeString) AS pos1) AS x
CROSS APPLY(SELECT CHARINDEX('/',d.SomeString,x.pos1+1) AS pos2) AS y
Detta är samma som följande, men mycket lättare att läsa (och skriva):
SELECT d.ID
,d.SomeString
,LEFT(d.SomeString,CHARINDEX('/',d.SomeString)-1)
,SUBSTRING(d.SomeString,CHARINDEX('/',d.SomeString)+1,CHARINDEX('/',d.SomeString,(CHARINDEX('/',d.SomeString)+1))-(CHARINDEX('/',d.SomeString)+1))
,SUBSTRING(d.SomeString,CHARINDEX('/',d.SomeString,(CHARINDEX('/',d.SomeString)+1))+1,1000)
FROM @dummy AS d
Ett exempel med XML-metoden .nodes()
DECLARE @dummy TABLE(SomeXML XML)
INSERT INTO @dummy VALUES
(N'<root>
<a>a1</a>
<a>a2</a>
<a>a3</a>
<b>Here is b!</b>
</root>');
SELECT All_a_nodes.value(N'.',N'nvarchar(max)')
FROM @dummy
CROSS APPLY SomeXML.nodes(N'/root/a') AS A(All_a_nodes);
Resultatet
a1
a2
a3
Och ett exempel på ett infogat funktionsanrop
CREATE FUNCTION dbo.TestProduceRows(@i INT)
RETURNS TABLE
AS
RETURN
SELECT TOP(@i) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Nr FROM master..spt_values
GO
CREATE TABLE dbo.TestData(ID INT IDENTITY, SomeString VARCHAR(100),Number INT);
INSERT INTO dbo.TestData VALUES
('Show me once',1)
,('Show me twice',2)
,('Me five times!',5);
SELECT *
FROM TestData
CROSS APPLY dbo.TestProduceRows(Number) AS x;
GO
DROP TABLE dbo.TestData;
DROP FUNCTION dbo.TestProduceRows;
Resultatet
1 Show me once 1 1
2 Show me twice 2 1
2 Show me twice 2 2
3 Me five times! 5 1
3 Me five times! 5 2
3 Me five times! 5 3
3 Me five times! 5 4
3 Me five times! 5 5