为什么COUNT(*)在SQL Server的所有表列上都需要SELECT权限?

Blu*_*uuu 9 sql t-sql sql-server sql-server-2017

我最近遇到了一个问题,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;
Run Code Online (Sandbox Code Playgroud)

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

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

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

Hon*_*ger 4

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

SELECT  COUNT(1)
FROM    (
            SELECT  P.name
            FROM    dbo.Product AS P
        ) AS t;
Run Code Online (Sandbox Code Playgroud)

当然,您需要对 Product.name 具有 SELECT 权限,但我从您的评论中了解到,这不应该成为问题。

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

SELECT  COUNT(1)
FROM    dbo.Product AS P
WHERE   P.name = P.name
    OR  P.name IS NULL 
Run Code Online (Sandbox Code Playgroud)

从想要不被允许的东西的用户(在本例中为实习生)的角度来看,上述内容非常有效。从 DBA 的角度来看,存在更好的方法来方便该用户。(摘自上面 Jeroen Mostert 的评论:)

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