มีสถานการณ์ต่าง ๆ ที่คุณไม่สามารถหลีกเลี่ยงหรือCROSS APPLYOUTER APPLY
พิจารณาว่าคุณมีสองตาราง
ตาราง MASTER
x------x--------------------x
| Id | Name |
x------x--------------------x
| 1 | A |
| 2 | B |
| 3 | C |
x------x--------------------x
ตารางรายละเอียด
x------x--------------------x-------x
| Id | PERIOD | QTY |
x------x--------------------x-------x
| 1 | 2014-01-13 | 10 |
| 1 | 2014-01-11 | 15 |
| 1 | 2014-01-12 | 20 |
| 2 | 2014-01-06 | 30 |
| 2 | 2014-01-08 | 40 |
x------x--------------------x-------x
ใช้ข้าม
มีหลายสถานการณ์ที่เราจำเป็นต้องเปลี่ยนเป็นด้วยINNER JOINCROSS APPLY
1. หากเราต้องการเข้าร่วม 2 ตารางกับTOP nผลลัพธ์ที่มีINNER JOINฟังก์ชันการทำงาน
พิจารณาถ้าเราต้องเลือกIdและNameจากMasterและวันที่สองวันที่ผ่านมาสำหรับแต่ละจากIdDetails table
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
INNER JOIN
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
ORDER BY CAST(PERIOD AS DATE)DESC
)D
ON M.ID=D.ID
แบบสอบถามด้านบนสร้างผลลัพธ์ต่อไปนี้
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
x------x---------x--------------x-------x
ดูว่าสร้างผลลัพธ์สำหรับสองวันที่แล้วโดยมีวันที่สองวันที่Idแล้วจากนั้นจึงรวมระเบียนเหล่านี้ในแบบสอบถามภายนอกIdเท่านั้นซึ่งไม่ถูกต้อง CROSS APPLYเพื่อให้บรรลุนี้เราจำเป็นต้องใช้
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
CROSS APPLY
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
WHERE M.ID=D.ID
ORDER BY CAST(PERIOD AS DATE)DESC
)D
และฟอร์มเขาตามผล
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-08 | 40 |
| 2 | B | 2014-01-06 | 30 |
x------x---------x--------------x-------x
นี่คือการทำงาน แบบสอบถามภายในCROSS APPLYสามารถอ้างอิงตารางด้านนอกซึ่งINNER JOINไม่สามารถทำได้ (แสดงข้อผิดพลาดในการคอมไพล์) เมื่อหาวันที่สองวันที่ผ่านมาเข้าร่วมจะทำภายในคือCROSS APPLYWHERE M.ID=D.ID
2. เมื่อเราต้องการINNER JOINฟังก์ชันโดยใช้ฟังก์ชัน
CROSS APPLYสามารถใช้แทนได้INNER JOINเมื่อเราต้องการผลลัพธ์จากMasterตารางและกfunction.
SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
CROSS APPLY dbo.FnGetQty(M.ID) C
และนี่คือฟังก์ชัน
CREATE FUNCTION FnGetQty
(
@Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE ID=@Id
)
ซึ่งสร้างผลลัพธ์ต่อไปนี้
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-11 | 15 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-06 | 30 |
| 2 | B | 2014-01-08 | 40 |
x------x---------x--------------x-------x
ใช้ภายนอก
1. หากเราต้องการเข้าร่วม 2 ตารางกับTOP nผลลัพธ์ที่มีLEFT JOINฟังก์ชันการทำงาน
พิจารณาว่าเราจำเป็นต้องเลือก Id และ Name หรือไม่Masterและสองวันสุดท้ายสำหรับแต่ละ Id จากDetailsตาราง
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
LEFT JOIN
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
ORDER BY CAST(PERIOD AS DATE)DESC
)D
ON M.ID=D.ID
ซึ่งก่อให้เกิดผลลัพธ์ต่อไปนี้
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | NULL | NULL |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
สิ่งนี้จะทำให้เกิดผลลัพธ์ที่ไม่ถูกต้องกล่าวคือจะนำข้อมูลวันที่ล่าสุดเพียงสองวันจากDetailsตารางโดยไม่คำนึงถึงIdแม้ว่าเราจะเข้าร่วมด้วยIdก็ตาม OUTER APPLYดังนั้นทางออกที่เหมาะสมคือการใช้
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
OUTER APPLY
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
WHERE M.ID=D.ID
ORDER BY CAST(PERIOD AS DATE)DESC
)D
ซึ่งสร้างผลลัพธ์ที่ต้องการดังต่อไปนี้
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-08 | 40 |
| 2 | B | 2014-01-06 | 30 |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
2. เมื่อเราต้องการLEFT JOINฟังก์ชันโดยใช้functions.
OUTER APPLYสามารถใช้แทนได้LEFT JOINเมื่อเราต้องการผลลัพธ์จากMasterตารางและกfunction.
SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
OUTER APPLY dbo.FnGetQty(M.ID) C
และฟังก์ชันจะอยู่ที่นี่
CREATE FUNCTION FnGetQty
(
@Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE ID=@Id
)
ซึ่งสร้างผลลัพธ์ต่อไปนี้
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-11 | 15 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-06 | 30 |
| 2 | B | 2014-01-08 | 40 |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
คุณสมบัติทั่วไปของCROSS APPLYและOUTER APPLY
CROSS APPLYหรือOUTER APPLYสามารถใช้เพื่อรักษาNULLค่าเมื่อเลิกใช้งานซึ่งสามารถใช้แทนกันได้
พิจารณาว่าคุณมีตารางด้านล่าง
x------x-------------x--------------x
| Id | FROMDATE | TODATE |
x------x-------------x--------------x
| 1 | 2014-01-11 | 2014-01-13 |
| 1 | 2014-02-23 | 2014-02-27 |
| 2 | 2014-05-06 | 2014-05-30 |
| 3 | NULL | NULL |
x------x-------------x--------------x
เมื่อคุณใช้UNPIVOTเพื่อนำFROMDATEAND TODATEไปยังคอลัมน์หนึ่งคอลัมน์จะกำจัดNULLค่าตามค่าเริ่มต้น
SELECT ID,DATES
FROM MYTABLE
UNPIVOT (DATES FOR COLS IN (FROMDATE,TODATE)) P
ซึ่งสร้างผลลัพธ์ด้านล่าง โปรดทราบว่าเราพลาดการบันทึกIdจำนวน3
x------x-------------x
| Id | DATES |
x------x-------------x
| 1 | 2014-01-11 |
| 1 | 2014-01-13 |
| 1 | 2014-02-23 |
| 1 | 2014-02-27 |
| 2 | 2014-05-06 |
| 2 | 2014-05-30 |
x------x-------------x
ในกรณีเช่นนี้CROSS APPLYหรือOUTER APPLYจะเป็นประโยชน์
SELECT DISTINCT ID,DATES
FROM MYTABLE
OUTER APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
ซึ่งสร้างผลลัพธ์ต่อไปนี้และคงไว้Idซึ่งค่าของมัน3
x------x-------------x
| Id | DATES |
x------x-------------x
| 1 | 2014-01-11 |
| 1 | 2014-01-13 |
| 1 | 2014-02-23 |
| 1 | 2014-02-27 |
| 2 | 2014-05-06 |
| 2 | 2014-05-30 |
| 3 | NULL |
x------x-------------x