首页 > 解决方案 > 为什么 COUNT(*) 需要对 SQL Server 上的所有表列具有 SELECT 权限?

问题描述

我最近遇到了COUNT(*)要求用户对表的每一列都具有选择权限的问题。即使'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

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

标签: sqlsql-servertsqlsql-server-2017

解决方案


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

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

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

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

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

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

您可以创建一个明确排除实习生不应该看到的列的视图,并授予 SELECT 权限。这样,查询就可以正常工作,而不必引入迂回和不直观的变通办法,并且您也不需要每列单独的 DENY 权限——您不必首先授予对基表的 SELECT 权限。


推荐阅读