Bạn cần CROSS APPLY
ở đây, nó có thể tham chiếu đến các tham chiếu bên ngoài, không cần truy vấn con hoặc CTE phiền phức:
select col1, col2
from table1 as outer_table
-- can also have multi-row values
cross apply (values (complex_expression_1) ) as v1 (col1)
cross apply (values (expression_referring_to_col1) ) as v2 (col2)
-- alternate syntax, select without from returns a single row
cross apply (select complex_expression_1 as col1 ) AS v1
cross apply (select expression_referring_to_col1 as col2 ) as v2
-- you can also do anything you like in there, can be one or multiple rows
cross apply (
select complex_expression_1 as col1
from othercomplexjoin as o
where o.join_column = outer_table.join_column
) AS v1
Một số thủ thuật khác mà bạn có thể thực hiện với APPLY
:
1. 1 trên mỗi nhóm của bảng con:
Giải pháp cổ điển cho "top 1 mỗi nhóm" là sử dụng row_number()
. Điều này thường có thể dẫn đến việc quét rất lớn, đặc biệt khi số lượng các giá trị bên ngoài khác biệt so với bảng con là nhỏ.
select
o.id,
lastPayment.Date
from order_header as o
join
( select *, row_number() over (partition by order_id order by date desc) as rn
from payments
) as lastPayment on ...
where lastPayment.rn = 1
Thay vào đó, chúng ta có thể làm:
select
o.id,
lastPayment.Date
from order_header as o
cross apply
( select top (1) *
from payments as p
where p.order_id = o.id
order by date desc
) as lastPayment
Lưu ý:OUTER APPLY
thay thế một cách khái niệm một phép nối bên trái, tức là trả về giá trị rỗng thay vì không có hàng.
2. Giải nén
select
o.id,
customer.*
from order_header as o
cross apply ( values -- This returns two rows for every order_header
( 'DeliveryCustomer', o.deliveryCustomer ),
( 'billingCustomer', o.billingCustomer )
) as customer (type, name)
3. Bùng nổ một hàng với số lần thay đổi:
Giả sử chúng ta muốn lấy một lượng và chia nó thành các hàng khác nhau. Nếu amount <= 50
sau đó là một hàng amount
, nếu > 50
sau đó là hai hàng, một trong số 50 và một trong các hàng còn lại:
select t.id, v.amount
from table as t
cross apply (
select case when amount > 50 then 50 else amount end as amount
union all
select amount - 50 -- note this row will not appear if amount < 50
where amount > 50
) v