为啥 COUNT(*) 需要对 SQL Server 上的所有表列具有 SELECT 权限?

Posted

技术标签:

【中文标题】为啥 COUNT(*) 需要对 SQL Server 上的所有表列具有 SELECT 权限?【英文标题】:Why does COUNT(*) require SELECT permission on all table columns on SQL Server?为什么 COUNT(*) 需要对 SQL Server 上的所有表列具有 SELECT 权限? 【发布时间】:2020-01-02 11:34:20 【问题描述】:

我最近遇到COUNT(*) 要求用户对表的每一列都有选择权限的问题。 尽管spec of 'COUNT(*)' 明确表示

它不使用任何特定列的信息。

它只返回结果中的行数。

因此,如果您想将表中的行数计算为受限用户,则会出现权限异常。

这是一个例子:

CREATE TABLE [Product]
([name] nvarchar(100) null, [price] float)

CREATE USER Intern WITHOUT LOGIN;
DENY SELECT ON [Product] (price) TO Intern;

EXECUTE AS Intern;

-- Fails with "The SELECT permission was denied on the column 'price' of the object 'Product'"
SELECT COUNT(*) FROM [Product];

REVERT;

经过一些测试,我发现即使 SELECT COUNT(1) FROM [Product] 不起作用。

有人可以解释这种行为背后的原因是什么吗? 什么是允许Intern 用户仍然获得Product 的准确计数的解决方法。

更新: 我对实习生可以使用的解决方法最感兴趣。因此,即使创建视图是管理员的最佳实践,实习生也没有此选项。

【问题讨论】:

你试过select count(1) ... 吗? select count(name) 工作吗? 最好的解决方案可能是创建一个明确排除 Intern 不应该看到的列的视图,并授予 SELECT 权限。这样,查询就可以正常工作,而不必引入迂回和不直观的变通办法,并且您也不需要每列单独的DENY 权限——您不必首先授予基表上的SELECT 权限。当您开始拒绝访问各个列时,COUNT(*) 可能不是唯一会造成不便的事情。 @JeroenMostert 你说得对,创建视图是最好的解决方案,我最感兴趣的是实习生如何解决这个问题,他大概无法创建视图。 @pascalsanchez 您将空名称转换为其他名称的解决方案。这些解决方案唯一不是最佳的部分是性能稍差。 【参考方案1】:

我不知道这种行为背后的原因,但有办法解决它:

SELECT  COUNT(1)
FROM    (
            SELECT  P.name
            FROM    dbo.Product AS P
        ) AS t;

当然,您需要对 Product.name 具有 SELECT 权限,但我从您的 cmets 收集到这应该不是问题。

附录,因为我同意这是意外行为。如果您执行以下操作,您还可以执行计数(如果您在 name 上拥有索引以及在 name 上拥有 SELECT 权限):

SELECT  COUNT(1)
FROM    dbo.Product AS P
WHERE   P.name = P.name
    OR  P.name IS NULL 

从想要一些他们不允许的东西的用户的角度来看,前面的工作很好(在这种情况下是实习生)。从 DBA 的角度来看,存在一种更好的方法来方便该用户。 (抄自上面 Jeroen Mostert 的评论:)

您可以创建一个明确排除实习生应该排除的列的视图 看不到,并授予 SELECT 权限。这样,查询工作 像往常一样,不必介绍迂回和不直观 解决方法,并且您不需要每列单独的 DENY 权限 或者——您不必授予对基表的 SELECT 权限 首先。

【讨论】:

我尝试了您的第二个解决方案,它确实有效(即使没有索引)。这真的很令人惊讶,似乎添加 WHERE 子句会以某种方式改变 count(*) 的含义?? 我试过SELECT COUNT(1) FROM dbo.Product WHERE P.name = P.name OR 1=1,它确实有效并且是正确的。

以上是关于为啥 COUNT(*) 需要对 SQL Server 上的所有表列具有 SELECT 权限?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 SQL SELECT 语句在 Java Spring boot 项目中不返回 COUNT() 结果?

为啥按 count(lab_results.testid) desc 排序在 C# 中不起作用 - 但在 SQL Server 中很好?

为啥 Linq-to-SQL 会添加不必要的 COUNT()?

为啥sql查询语句中的count(*)等聚合函数可以放在having后面,而不能放在where后面?

为啥 SQL 计数(*)与 SQL 计数(数字)存在行为差异

为啥 dataset.count() 比 rdd.count() 快?