可以存储一个选择列的值并将其用于下一列吗?

Use*_*123 2 t-sql sql-server

是否可以存储或缓存属于一个选择列的一部分的值,然后用于下一个选择列?例如,

select 
       FirstColumn = (complex query returns a value based on ThirdColumn),
       SecondColumn = (uses the same value returned from above + does some additional calculations.)
from SomeTable
Run Code Online (Sandbox Code Playgroud)

是否可以做类似的事情,这样我就不必两次编写相同的复杂查询?

Cha*_*ace 7

你需要CROSS APPLY在这里,它可以引用外部引用,不需要烦人的子查询或 CTE:

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
Run Code Online (Sandbox Code Playgroud)

您还可以使用一些其他技巧APPLY

1.子表每组前1名:

“每组前 1 名”的经典解决方案是使用row_number(). 这通常会导致大量扫描,尤其是当不同外部值的数量相对于子表而言较小时。

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
Run Code Online (Sandbox Code Playgroud)

相反,我们可以这样做:

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
Run Code Online (Sandbox Code Playgroud)

注意:OUTER APPLY概念上替换左连接,即返回空值而不是不返回任何行。


2. 不旋转

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)
Run Code Online (Sandbox Code Playgroud)

3. 将一行展开可变次数:

假设我们想要获取一个金额,并将其分成不同的行。如果amount <= 50则 一行amount,如果> 50则两行,其中 50 行之一,其余行之一:

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
Run Code Online (Sandbox Code Playgroud)

  • 添加了更多可能的语法 (2认同)