如何移除WHERE子句中的标量函数?合并SQL同表查询的最佳方案
Great catch on avoiding scalar functions in the WHERE clause—those can be total performance killers since they execute row-by-row instead of leveraging efficient set-based operations. Let’s rewrite your query by integrating the fGetCount logic directly into the main query, using approaches the database optimizer can optimize effectively.
First, let’s break down what fGetCount(IP.IPId) = 0 actually means: there are no rows in IPCTable where IsCopy = 1 and IPId matches the current IP.IPId. We can translate this into set-based logic in two common, efficient ways:
Option 1: Use NOT EXISTS (Most Efficient for "No Match" Checks)
NOT EXISTS is usually the best choice here because the database stops searching as soon as it finds a matching row (unlike counting all rows). It’s clean and optimized well by most SQL engines.
SELECT DISTINCT IP.IRId FROM cmp.NPTable NP INNER JOIN IPTable IP ON IP.IPtId = NP.IPd LEFT JOIN IPCTable IPC ON IPC.IPId = NP.IPId AND IPC.IsNC = 1 WHERE NP.PCNId = @PCNId AND NOT EXISTS ( SELECT 1 -- We don't need actual data, just existence check FROM IPCTable WHERE IsCopy = 1 AND IPId = IP.IPId )
Option 2: Use LEFT JOIN + IS NULL
If you prefer using joins over subqueries, this approach works by left-joining to the IsCopy = 1 subset of IPCTable and checking for no matches (indicated by NULL values):
SELECT DISTINCT IP.IRId FROM cmp.NPTable NP INNER JOIN IPTable IP ON IP.IPtId = NP.IPd LEFT JOIN IPCTable IPC ON IPC.IPId = NP.IPId AND IPC.IsNC = 1 LEFT JOIN IPCTable IPC_Copy ON IPC_Copy.IPId = IP.IPId AND IPC_Copy.IsCopy = 1 WHERE NP.PCNId = @PCNId AND IPC_Copy.IPId IS NULL
Bonus: Boost Performance with Indexing
To make either of these queries run even faster, add a composite index on IPCTable that covers the columns we’re filtering and joining on:
CREATE INDEX IX_IPCTable_IPId_IsCopy ON IPCTable(IPId, IsCopy);
This index lets the database quickly look up whether any IsCopy = 1 rows exist for a given IPId, without scanning the entire table.
Why This Is Better Than the Scalar Function
Your original query calls fGetCount once for every row returned by the joins, which can slow things down significantly with large datasets. By using set-based logic instead, the database can optimize the entire query as a single operation, reducing redundant work and leveraging indexes effectively.
内容的提问来源于stack exchange,提问作者John

