在仅使用文字值的WHERE子句中替换ISNULL()的不同方法有哪些?


55

这不是关于:

这不是接受用户输入或使用变量的全部查询的问题。

这是严格,对查询ISNULL()中使用WHERE条款,以取代NULL与金丝雀值值进行比较的谓语,而不同的方式来重写这些查询以优化搜索在SQL Server中。

你为什么不坐在那儿?

我们的示例查询针对SQL Server 2016上Stack Overflow数据库的本地副本,并查找NULL年龄小于或小于18的用户。

SELECT COUNT(*)
FROM dbo.Users AS u
WHERE ISNULL(u.Age, 17) < 18;

查询计划显示对相当周到的非聚集索引的扫描。

坚果类

扫描操作符显示(由于在SQL Server的最新版本中对实际执行计划XML的添加),我们读取了每一个臭行。

坚果类

总体而言,我们进行9157次读取,并占用大约半秒的CPU时间:

Table 'Users'. Scan count 1, logical reads 9157, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 485 ms,  elapsed time = 483 ms.

问题:有 什么方法可以重写此查询以使其更高效,甚至可以保存。

随时提供其他建议。我认为我的答案不一定答案,并且有足够多的聪明人提出一些更好的选择。

如果您想在自己的计算机上玩,请前往此处下载SO数据库

谢谢!

Answers:


57

答案部分

有多种方法可以使用不同的T-SQL构造来重写它。我们将看一下优缺点,并在下面进行整体比较。

首先:使用OR

SELECT COUNT(*)
FROM dbo.Users AS u
WHERE u.Age < 18
OR u.Age IS NULL;

使用OR为我们提供了更有效的Seek计划,该计划可以读取所需的确切行数,但是它将技术世界称为a whole mess of malarkey查询计划的内容。

坚果类

还要注意,Seek在这里执行了两次,从图形运算符中应该更明显:

坚果类

Table 'Users'. Scan count 2, logical reads 8233, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 469 ms,  elapsed time = 473 ms.

第二篇:在UNION ALL 我们的查询中使用派生表也可以这样重写

SELECT SUM(Records)
FROM 
(
    SELECT COUNT(Id)
    FROM dbo.Users AS u
    WHERE u.Age < 18

    UNION ALL

    SELECT COUNT(Id)
    FROM dbo.Users AS u
    WHERE u.Age IS NULL
) x (Records);

这样就产生了相同类型的计划,而少了一些恶意键,并且对于寻找(搜索到)该索引多少次的诚实度有了更明显的认识。

坚果类

它执行与查询相同的读取次数(8233)OR,但是节省了大约100毫秒的CPU时间。

CPU time = 313 ms,  elapsed time = 315 ms.

但是,您在这里必须非常小心,因为如果该计划尝试并行执行,那么两个单独的COUNT操作将被序列化,因为它们每个都被视为全局标量集合。如果我们使用跟踪标志8649强制执行并行计划,问题将变得很明显。

SELECT SUM(Records)
FROM 
(
    SELECT COUNT(Id)
    FROM dbo.Users AS u
    WHERE u.Age < 18

    UNION ALL

    SELECT COUNT(Id)
    FROM dbo.Users AS u
    WHERE u.Age IS NULL
) x (Records)
OPTION(QUERYTRACEON 8649);

坚果类

可以通过稍微更改查询来避免这种情况。

SELECT SUM(Records)
FROM 
(
    SELECT 1
    FROM dbo.Users AS u
    WHERE u.Age < 18

    UNION ALL

    SELECT 1
    FROM dbo.Users AS u
    WHERE u.Age IS NULL
) x (Records)   
OPTION(QUERYTRACEON 8649);

现在,执行Seek的两个节点都完全并行化,直到我们命中串联运算符。

坚果类

就其价值而言,完全并行的版本具有一些不错的好处。以大约100次以上的读取和大约90ms的额外CPU时间为代价,经过的时间减少到93ms。

Table 'Users'. Scan count 12, logical reads 8317, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 500 ms,  elapsed time = 93 ms.

那CROSS APPLY呢? 没有魔力,没有答案是完整的CROSS APPLY

不幸的是,我们遇到了更多的问题COUNT

SELECT SUM(Records)
FROM dbo.Users AS u 
CROSS APPLY 
(
    SELECT COUNT(Id)
    FROM dbo.Users AS u2 
    WHERE u2.Id = u.Id
    AND u2.Age < 18

    UNION ALL

    SELECT COUNT(Id)
    FROM dbo.Users AS u2 
    WHERE u2.Id = u.Id 
    AND u2.Age IS NULL
) x (Records);

这个计划太可怕了。这是您最后一次出现在圣帕特里克节时最终要制定的计划。尽管很好地并行,但由于某种原因,它正在扫描PK / CX。真是的 该计划的成本为2198美元。

坚果类

Table 'Users'. Scan count 7, logical reads 31676233, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 29532 ms,  elapsed time = 5828 ms.

这是一个奇怪的选择,因为如果我们强迫它使用非聚集索引,则成本将大大下降至1798查询美元。

SELECT SUM(Records)
FROM dbo.Users AS u 
CROSS APPLY 
(
    SELECT COUNT(Id)
    FROM dbo.Users AS u2 WITH (INDEX(ix_Id_Age))
    WHERE u2.Id = u.Id
    AND u2.Age < 18

    UNION ALL

    SELECT COUNT(Id)
    FROM dbo.Users AS u2 WITH (INDEX(ix_Id_Age))
    WHERE u2.Id = u.Id 
    AND u2.Age IS NULL
) x (Records);

嘿,寻求!在那儿检查你。还要注意,有了的魔力CROSS APPLY,我们不需要费劲就能拥有几乎完全并行的计划。

坚果类

Table 'Users'. Scan count 5277838, logical reads 31685303, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 27625 ms,  elapsed time = 4909 ms.

如果没有任何COUNT东西,交叉申请的结果会更好。

SELECT SUM(Records)
FROM dbo.Users AS u
CROSS APPLY 
(
    SELECT 1
    FROM dbo.Users AS u2
    WHERE u2.Id = u.Id
    AND u2.Age < 18

    UNION ALL

    SELECT 1
    FROM dbo.Users AS u2
    WHERE u2.Id = u.Id 
    AND u2.Age IS NULL
) x (Records);

该计划看起来不错,但是读取和CPU并没有改善。

坚果类

Table 'Users'. Scan count 20, logical reads 17564, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 4844 ms,  elapsed time = 863 ms.

将交叉重写为派生联接所产生的结果完全相同。我不会重新发布查询计划和统计信息-它们确实没有改变。

SELECT COUNT(u.Id)
FROM dbo.Users AS u
JOIN 
(
    SELECT u.Id
    FROM dbo.Users AS u
    WHERE u.Age < 18

    UNION ALL

    SELECT u.Id
    FROM dbo.Users AS u
    WHERE u.Age IS NULL
) x ON x.Id = u.Id;

关系代数:要彻底讲解,并防止Joe Celko困扰我的梦想,我们至少需要尝试一些奇怪的关系事物。没什么了!

尝试 INTERSECT

SELECT COUNT(*)
FROM dbo.Users AS u
WHERE NOT EXISTS ( SELECT u.Age WHERE u.Age >= 18
                   INTERSECT
                   SELECT u.Age WHERE u.Age IS NOT NULL );

坚果类

Table 'Users'. Scan count 1, logical reads 9157, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 1094 ms,  elapsed time = 1090 ms.

这是尝试 EXCEPT

SELECT COUNT(*)
FROM dbo.Users AS u
WHERE NOT EXISTS ( SELECT u.Age WHERE u.Age >= 18
                   EXCEPT
                   SELECT u.Age WHERE u.Age IS NULL);

坚果类

Table 'Users'. Scan count 7, logical reads 9247, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 2126 ms,  elapsed time = 376 ms.

可能还有其他写这些方法的方法,但是我会让那些可能会使用EXCEPT并且INTERSECT比我更常使用的人留给他们。

如果您真的只需要计数, 我会COUNT在查询中使用它作为速记(请阅读:我很懒,有时无法提出更多涉及的场景)。如果只需要计数,则可以使用CASE表达式执行几乎相同的操作。

SELECT SUM(CASE WHEN u.Age < 18 THEN 1
                WHEN u.Age IS NULL THEN 1
                ELSE 0 END) 
FROM dbo.Users AS u

SELECT SUM(CASE WHEN u.Age < 18 OR u.Age IS NULL THEN 1
                ELSE 0 END) 
FROM dbo.Users AS u

这些都具有相同的计划,并具有相同的CPU和读取特性。

坚果类

Table 'Users'. Scan count 1, logical reads 9157, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 719 ms,  elapsed time = 719 ms.

赢家? 在我的测试中,在派生表上使用SUM进行强制并行计划的效果最佳。是的,可以通过添加几个过滤索引来说明这两个谓词来辅助其中的许多查询,但是我想对其他谓词进行一些试验。

SELECT SUM(Records)
FROM 
(
    SELECT 1
    FROM dbo.Users AS u
    WHERE u.Age < 18

    UNION ALL

    SELECT 1
    FROM dbo.Users AS u
    WHERE u.Age IS NULL
) x (Records)   
OPTION(QUERYTRACEON 8649);

谢谢!


1
这些NOT EXISTS ( INTERSECT / EXCEPT )查询可以不包含以下INTERSECT / EXCEPT部分而工作:WHERE NOT EXISTS ( SELECT u.Age WHERE u.Age >= 18 );另一种方式-使用EXCEPT:(SELECT COUNT(*) FROM (SELECT UserID FROM dbo.Users EXCEPT SELECT UserID FROM dbo.Users WHERE u.Age >= 18) AS u ; 其中UserID是PK或任何唯一的非null列)。
ypercubeᵀᴹ

这个测试了吗?SELECT result = (SELECT COUNT(*) FROM dbo.Users AS u WHERE u.Age < 18) + (SELECT COUNT(*) FROM dbo.Users AS u WHERE u.Age IS NULL) ;抱歉,如果我错过了您测试过的百万个版本!
ypercubeᵀᴹ

@ypercubeᵀᴹ 这是那个的计划。稍有不同,但与UNION ALL计划具有相似的特征(360ms CPU,11k读取)。
埃里克·达令

嘿,埃里克(Erik),只是在漫游sql的世界,突然弹出“计算列”的字样来惹恼您。<3
坩埚

17

我并不是为了仅仅为一个表恢复110 GB数据库的游戏,所以我创建了自己的数据。年龄分布应该与Stack Overflow上的内容匹配,但显然表格本身不匹配。我认为这不是一个太大的问题,因为无论如何查询都会命中索引。我正在使用SQL Server 2016 SP1的4 CPU计算机上进行测试。需要注意的一件事是,对于快速完成此查询的查询,重要的是不要包含实际的执行计划。这会使事情变慢很多。

我首先介绍了Erik出色答案中的一些解决方案。为此:

SELECT SUM(Records)
FROM 
(
    SELECT COUNT(Id)
    FROM dbo.Users AS u
    WHERE u.Age < 18

    UNION ALL

    SELECT COUNT(Id)
    FROM dbo.Users AS u
    WHERE u.Age IS NULL
) x (Records);

我从sys.dm_exec_sessions进行了10多次试验,获得了以下结果(查询对我来说自然是并行的):

╔══════════╦════════════════════╦═══════════════╗
 cpu_time  total_elapsed_time  logical_reads 
╠══════════╬════════════════════╬═══════════════╣
     3532                 975          60830 
╚══════════╩════════════════════╩═══════════════╝

对于Erik更好的查询实际上在我的机器上表现较差:

SELECT SUM(Records)
FROM 
(
    SELECT 1
    FROM dbo.Users AS u
    WHERE u.Age < 18

    UNION ALL

    SELECT 1
    FROM dbo.Users AS u
    WHERE u.Age IS NULL
) x (Records)   
OPTION(QUERYTRACEON 8649);

10个试验的结果:

╔══════════╦════════════════════╦═══════════════╗
 cpu_time  total_elapsed_time  logical_reads 
╠══════════╬════════════════════╬═══════════════╣
     5704                1636          60850 
╚══════════╩════════════════════╩═══════════════╝

我无法立即解释为什么会如此糟糕,但尚不清楚为什么我们要强制查询计划中的几乎每个运算符并行执行。在原始计划中,我们有一个串行区域,该区域查找带有的所有行AGE < 18。只有几千行。在我的机器上,对于该部分查询,我有9次逻辑读取,并报告了9 ms的CPU时间和经过时间。对于行的全局聚合,还有一个串行区域,AGE IS NULL但每个DOP仅处理一行。在我的机器上,这只是四行。

我的要点是,最优化查询中带有NULLfor的行的部分是最重要的,Age因为其中有数百万行。我无法创建一个索引,该索引所包含的数据页面要比该列上的简单页面压缩索引少。我假设每行有一个最小的索引大小,或者用我尝试的技巧无法避免很多索引空间。因此,如果我们坚持使用大约相同数量的逻辑读取来获取数据,那么使其更快的唯一方法就是使查询更加并行,但这需要与使用TF的Erik查询不同的方式来完成。 8649.在上面的查询中,CPU时间与经过时间的比率为3.62,这非常好。理想的是我的机器上的比率为4.0。

一个可能的改进领域是将工作更均匀地分配到各个线程之间。在下面的屏幕截图中,我们可以看到我的一个CPU决定休息一下:

懒线程

索引扫描是可并行实现的少数几个运算符之一,我们对行如何分配到线程一无所知。也有一定的机会,但是我一直都看到一个工作不足的线程。解决此问题的一种方法是采用并行方法:在嵌套循环连接的内部。嵌套循环内部的任何内容都将以串行方式实现,但是许多串行线程可以并发运行。只要我们获得了一种有利的并行分配方法(例如轮询),就可以精确控制向每个线程发送多少行。

我正在使用DOP 4运行查询,因此我需要将NULL表中的行平均分成四个存储桶。一种方法是在计算列上创建一堆索引:

ALTER TABLE dbo.Users
ADD Compute_bucket_0 AS (CASE WHEN Age IS NULL AND Id % 4 = 0 THEN 1 ELSE NULL END),
Compute_bucket_1 AS (CASE WHEN Age IS NULL AND Id % 4 = 1 THEN 1 ELSE NULL END),
Compute_bucket_2 AS (CASE WHEN Age IS NULL AND Id % 4 = 2 THEN 1 ELSE NULL END),
Compute_bucket_3 AS (CASE WHEN Age IS NULL AND Id % 4 = 3 THEN 1 ELSE NULL END);

CREATE INDEX IX_Compute_bucket_0 ON dbo.Users (Compute_bucket_0) WITH (DATA_COMPRESSION = PAGE);
CREATE INDEX IX_Compute_bucket_1 ON dbo.Users (Compute_bucket_1) WITH (DATA_COMPRESSION = PAGE);
CREATE INDEX IX_Compute_bucket_2 ON dbo.Users (Compute_bucket_2) WITH (DATA_COMPRESSION = PAGE);
CREATE INDEX IX_Compute_bucket_3 ON dbo.Users (Compute_bucket_3) WITH (DATA_COMPRESSION = PAGE);

我不太确定为什么四个单独的索引比一个索引快一点,但这就是我在测试中发现的索引。

为了获得并行的嵌套循环计划,我将使用未记录的跟踪标志8649。我还要奇怪地编写代码,以鼓励优化器不要处理超出必要的行数。以下是一种看起来效果很好的实现:

SELECT SUM(t.cnt) + (SELECT COUNT(*) FROM dbo.Users AS u WHERE u.Age < 18)
FROM 
(VALUES (0), (1), (2), (3)) v(x)
CROSS APPLY 
(
    SELECT COUNT(*) cnt 
    FROM dbo.Users 
    WHERE Compute_bucket_0 = CASE WHEN v.x = 0 THEN 1 ELSE NULL END

    UNION ALL

    SELECT COUNT(*) cnt 
    FROM dbo.Users 
    WHERE Compute_bucket_1 = CASE WHEN v.x = 1 THEN 1 ELSE NULL END

    UNION ALL

    SELECT COUNT(*) cnt 
    FROM dbo.Users 
    WHERE Compute_bucket_2 = CASE WHEN v.x = 2 THEN 1 ELSE NULL END

    UNION ALL

    SELECT COUNT(*) cnt 
    FROM dbo.Users 
    WHERE Compute_bucket_3 = CASE WHEN v.x = 3 THEN 1 ELSE NULL END
) t
OPTION (QUERYTRACEON 8649);

十项试验的结果:

╔══════════╦════════════════════╦═══════════════╗
 cpu_time  total_elapsed_time  logical_reads 
╠══════════╬════════════════════╬═══════════════╣
     3093                 803          62008 
╚══════════╩════════════════════╩═══════════════╝

通过该查询,我们得到的CPU与经过时间之比为3.85!我们从运行时缩短了17毫秒,仅花费了4个计算列和索引即可完成!每个线程处理的行数几乎接近相同的总数,因为每个索引的行数非常接近,并且每个线程仅扫描一个索引:

分工明确

最后要注意的是,我们还可以单击“简单”按钮,并向该Age列添加非集群的CCI :

CREATE NONCLUSTERED COLUMNSTORE INDEX X_NCCI ON dbo.Users (Age);

以下查询将在我的计算机上在3毫秒内完成:

SELECT COUNT(*)
FROM dbo.Users AS u
WHERE u.Age < 18 OR u.Age IS NULL;

那将很难被击败。


7

尽管我没有Stack Overflow数据库的本地副本,但是我可以尝试一些查询。我的想法是从系统目录视图中获取用户数量(而不是直接从基础表中获取行数量)。然后,获取符合(或可能不符合)Erik的条件的行数,并进行一些简单的数学运算。

我使用了Stack Exchange Data Explorer(与SET STATISTICS TIME ON;和一起SET STATISTICS IO ON;)来测试查询。作为参考,以下是一些查询和CPU / IO统计信息:

查询1

--Erik's query From initial question.
SELECT COUNT(*)
FROM dbo.Users AS u
WHERE ISNULL(u.Age, 17) < 18;

SQL Server执行时间:CPU时间= 0毫秒,经过的时间= 0毫秒。(返回1行)

表“用户”。扫描计数17,逻辑读201567,物理读0,预读2740,lob逻辑读0,lob物理读0,lob预读0。

SQL Server执行时间:CPU时间= 1829 ms,经过的时间= 296 ms。

查询2

--Erik's "OR" query.
SELECT COUNT(*)
FROM dbo.Users AS u
WHERE u.Age < 18
OR u.Age IS NULL;

SQL Server执行时间:CPU时间= 0毫秒,经过的时间= 0毫秒。(返回1行)

表“用户”。扫描计数17,逻辑读201567,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

SQL Server执行时间:CPU时间= 2500毫秒,经过的时间= 147毫秒。

查询3

--Erik's derived tables/UNION ALL query.
SELECT SUM(Records)
FROM 
(
    SELECT COUNT(Id)
    FROM dbo.Users AS u
    WHERE u.Age < 18

    UNION ALL

    SELECT COUNT(Id)
    FROM dbo.Users AS u
    WHERE u.Age IS NULL
) x (Records);

SQL Server执行时间:CPU时间= 0毫秒,经过的时间= 0毫秒。(返回1行)

表“用户”。扫描计数34,逻辑读取403134,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob提前读取0。

SQL Server执行时间:CPU时间= 3156 ms,经过的时间= 215 ms。

第一次尝试

这比我在这里列出的所有Erik查询都要慢...至少在经过时间方面。

SELECT SUM(p.Rows)  -
  (
    SELECT COUNT(*)
    FROM dbo.Users AS u
    WHERE u.Age >= 18
  ) 
FROM sys.objects o
JOIN sys.partitions p
    ON p.object_id = o.object_id
WHERE p.index_id < 2
AND o.name = 'Users'
AND SCHEMA_NAME(o.schema_id) = 'dbo'
GROUP BY o.schema_id, o.name

SQL Server执行时间:CPU时间= 0毫秒,经过的时间= 0毫秒。(返回1行)

表“工作表”。扫描计数0,逻辑读0,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。表'sysrowsets'。扫描计数2,逻辑读10,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。表'sysschobjs'。扫描计数1,逻辑读4,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。表'Users'。扫描计数1,逻辑读201567,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

SQL Server执行时间:CPU时间= 593毫秒,经过的时间= 598毫秒。

第二次尝试

在这里,我选择了一个变量来存储用户总数(而不是子查询)。与第一次尝试相比,扫描次数从1增加到17。逻辑读取保持不变。但是,经过的时间大大减少了。

DECLARE @Total INT;

SELECT @Total = SUM(p.Rows)
FROM sys.objects o
JOIN sys.partitions p
    ON p.object_id = o.object_id
WHERE p.index_id < 2
AND o.name = 'Users'
AND SCHEMA_NAME(o.schema_id) = 'dbo'
GROUP BY o.schema_id, o.name

SELECT @Total - COUNT(*)
FROM dbo.Users AS u
WHERE u.Age >= 18

SQL Server执行时间:CPU时间= 0毫秒,经过的时间= 0毫秒。表“工作表”。扫描计数0,逻辑读0,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。表'sysrowsets'。扫描计数2,逻辑读10,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。表'sysschobjs'。扫描计数1,逻辑读为4,物理读为0,预读为0,lob逻辑读为0,lob物理读为0,lob预读为0。

SQL Server执行时间:CPU时间= 0毫秒,经过的时间= 1毫秒。(返回1行)

表“用户”。扫描计数17,逻辑读201567,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

SQL Server执行时间:CPU时间= 1471 ms,经过的时间= 98 ms。

其他说明: 如下所述,Stack Exchange Data Explorer上不允许使用DBCC TRACEON:

用户'STACKEXCHANGE \ svc_sede'没有运行DBCC TRACEON的权限。


1
他们可能没有与我相同的索引,因此有所不同。而且,谁知道呢?也许我的家庭服务器使用的是更好的硬件;)不错的答案!
艾瑞克·达林

您应该在第一次尝试时使用以下查询(速度会更快,因为它消除了sys.objects-headhead的大部分内容): SELECT SUM(p.Rows) - (SELECT COUNT(*) FROM dbo.Users AS u WHERE u.Age >= 18 ) FROM sys.partitions p WHERE p.index_id < 2 AND p.object_id = OBJECT_ID('dbo.Users')
Thomas Franz

PS:请注意,内存中索引(非哈希散列)没有索引ID = 0/1,就像普通堆/聚集索引一样)
Thomas Franz

1

使用变量?

declare @int1 int = ( select count(*) from table_1 where bb <= 1 )
declare @int2 int = ( select count(*) from table_1 where bb is null )
select @int1 + @int2;

每条评论可以跳过变量

SELECT (select count(*) from table_1 where bb <= 1) 
     + (select count(*) from table_1 where bb is null);

3
另外:SELECT (select count(*) from table_1 where bb <= 1) + (select count(*) from table_1 where bb is null);
ypercubeᵀᴹ

3
在检查CPU和IO时可能想尝试一下。提示:这与Erik的答案之一相同。
布伦特·奥扎尔

0

好用 SET ANSI_NULLS OFF;

SET ANSI_NULLS OFF; 
SET STATISTICS TIME ON;
SET STATISTICS IO ON;

SELECT COUNT(*)
FROM dbo.Users AS u
WHERE age=NULL or age<18

Table 'Users'. Scan count 17, logical reads 201567

 SQL Server Execution Times:
 CPU time = 2344 ms,  elapsed time = 166 ms.

这只是我突然想到的事情,只需在https://data.stackexchange.com中执行此操作即可

但是效率不如@blitz_erik


0

一个简单的解决方案是计算count(*)-count(年龄> = 18):

SELECT
    (SELECT COUNT(*) FROM Users) -
    (SELECT COUNT(*) FROM Users WHERE Age >= 18);

要么:

SELECT COUNT(*)
     - COUNT(CASE WHEN Age >= 18)
FROM Users;

结果在这里

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.