问题
Is it possible to store or cache values that are part of one select column and then be used for the next one? For example,
select
FirstColumn = (complex query returns a value based on ThirdColumn),
SecondColumn = (uses the same value returned from above + does some additional calculations.)
from SomeTable
Is it possible to do something like that so I don't have to write the same complex query twice?
回答1:
You need CROSS APPLY
here, it can refer to outer references, no annoying subqueries or CTEs needed:
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
Some more tricks you can do with APPLY
:
1. Top 1 per group of child table:
A classic solution to the "top 1 per group" is to use row_number()
. This can often result in huge scans, especially when the number of distinct outer values is small relative to the child table.
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
Instead we can do:
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
Note: OUTER APPLY
conceptually replaces a left join, i.e. returns nulls instead of no rows.
2. Unpivoting
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. Exploding out a row a variable number of times:
Say we want to take an amount, and split it into different rows. If the amount <= 50
then one row of amount
, if > 50
then two rows, one of 50 and one of the rest:
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
回答2:
I think in that case, you should use CTE (common table expression). for example
;WITH CTE1 AS
(
select col1 from table ---- your query
),
CTE2 as
(
select col2 from table --- your query
)
select col1, Col2
from CTE1 join CTE2 on --- do anything you want
回答3:
One way to do it is select the resultset and work from there.
This works by selecting the firstcolumn with all its complex stuff and return it in a resultset called t
.
select t.FirstColumn,
from ( select FirstColumn --(complex query returns a value based on ThirdColumn)
from SomeTable
) t
Then you can very simply select from t
and it will return FirstColumn
as if it was a normal simple column, so you can add to it without having to repeat the complex stuff.
select t.FirstColumn,
secondcolum (that uses t.FirstColum as it where a normal field)
from ( select FirstColumn
from SomeTable
) t
An example:
select t.FirstColumn,
(t.FirstColum / 123.0) * 50 as SecondColumn
from ( select (s.A + s.B) * s.C as FirstColumn
from sometable s
) t
As you can see, I don't need to repeat the calculation for t.Firstcolumn to add stuff to it for my SecondColumn.
Another option is to use cross apply
(there is an explanation in another answer here so I won't repeat that).
But do test which solution performs best for you, cross apply could have a performance penalty in some cases, and so can my solution. So just test what works best for you.
回答4:
A sub-query is the simplest form. You can nest sub-queries to any level you like performing calculations along the way:
select
ComplexValue1 as FirstColumn
, ComplexValue1 + ComplexValue2 as SecondColumn
from (
select
{complex query returns a value} ComplexValue1
, {complex query returns a value} ComplexValue2
, {any other required columns}
from SomeTable
) X;
Note: a CTE is essentially the same thing just prettier (maybe).
A lateral join is another option as it allows you to reference columns in the base table and perform calculations on them which are then available for use in the resultset:
select
X.ComplexValue1 as FirstColumn
, X.ComplexValue1 + ST.ComplexValue2 as SecondColumn
from SomeTable ST
cross apply (select {complex query returns a value referencing ST}) as X (ComplexValue1);
来源:https://stackoverflow.com/questions/65818438/possible-to-store-value-of-one-select-column-and-use-it-for-the-next-one