问题
Imagine the following simple query, where I get a list of all users that were created within the last 3 days, the logic or example is not important
SELECT
...
, DATEDIFF(DAY, U.DateCreated, GETUTCDATE())
...
FROM
dbo.AspNetUsers U
WHERE
DATEDIFF(DAY, U.DateCreated, GETUTCDATE()) < 3
I've repeated some code DATEDIFF(DAY, U.DateCreated, GETUTCDATE()) < 3
which, when I have much more complicated examples of the above I don't want to maintain twice or however many times I need that logic.
How should one go about dealing with this with performance in mind?
Thanks
回答1:
If you have performance in mind, then you'd better repeat expressions when needed. Specifically, don't try to put them in a user-defined functions. They are known to make queries slow in SQL Server.
Having said that, there are at least two methods in SQL Server to make queries more readable without affecting the performance:
- CTE
- CROSS APPLY
Example for use of CTE:
WITH
CTE
AS
(
SELECT
...
, DATEDIFF(DAY, U.DateCreated, GETUTCDATE()) AS CalculatedColumn
...
FROM
dbo.AspNetUsers U
)
SELECT
...
CalculatedColumn
...
FROM CTE
WHERE
CalculatedColumn < 3
;
Example for using CROSS APPLY
:
Instead of repeating parts of the formula in the following query:
SELECT
ColA + ColB AS ColSum
,ColA - ColB AS ColDiff
,(ColA + ColB) * (ColA - ColB) AS Result
,(ColA + ColB) * (ColA - ColB) * 100.0 AS Percentage
FROM Table
You can use CROSS APPLY
to write it like this:
SELECT
ColSum
,ColDiff
,Result
,Result * 100.0 AS Percentage
FROM
Table
CROSS APPLY
(
SELECT
ColA + ColB AS ColSum
,ColA - ColB AS ColDiff
) AS A1
CROSS APPLY
(
SELECT ColSum * ColDiff AS Result
) AS A2
By the way, speaking about performance,
WHERE DATEDIFF(DAY, U.DateCreated, GETUTCDATE()) < 3
is terrible, because it can't use index on DateCreated
(because you wrapped the column in a function).
You'd better rewrite it as something like
WHERE U.DateCreated > DATEADD(DAY, -3, GETUTCDATE())
回答2:
I would use CROSS APPLY
with VALUES
:
SELECT . . ., v.diff
FROM dbo.AspNetUsers U CROSS APPLY
(VALUES (DATEDIFF(DAY, U.DateCreated, GETUTCDATE()))
) v(diff)
WHERE v.diff < 3;
You can add multiple expressions to the values clause:
SELECT . . ., v.diff
FROM dbo.AspNetUsers U CROSS APPLY
(VALUES (DATEDIFF(DAY, U.DateCreated, GETUTCDATE()),
YEAR(U.DateCreated)
)
) v(diff, yyyy)
WHERE v.diff < 3;
回答3:
Similar approach to Gordon Linoff's, but using a SELECT subquery:
SELECT . . ., v.diff
FROM dbo.AspNetUsers U CROSS APPLY
(SELECT DATEDIFF(DAY, U.DateCreated, GETUTCDATE()) as diff
) v
WHERE v.diff < 3;
回答4:
In this clause WHERE DATEDIFF(DAY, U.DateCreated, GETUTCDATE()) < 3
, Index will not get seeked. so you need to apply where clause with,
WHERE U.DateCreated >= DATEADD(DAY, -3, GETUTCDATE())
.
Now, Your index over U.DateCreated will get seek and better performs. and now you can use DATETIFF type of functions in SELECT Statement.
来源:https://stackoverflow.com/questions/54993580/how-to-make-repeating-custom-expressions-in-sql-queries-maintainable