【问题标题】:Using IF EXISTS with a stored procedure as argument使用带有存储过程的 IF EXISTS 作为参数
【发布时间】:2016-03-07 17:13:50
【问题描述】:

我有一个名为“authenticate”的存储过程,如果提供了正确的用户名和密码,它会返回用户配置文件记录。现在,如果“身份验证”存储过程向我返回了一条记录,我正在尝试获取系统中的所有用户,否则什么也不返回。我正在尝试这样的事情:

IF EXISTS EXECUTE authenticate @UserName, @Password
BEGIN    
    SELECT * from Users;
END

我收到错误:关键字“EXECUTE”附近的语法不正确。 任何想法我做错了什么?

【问题讨论】:

  • 您对设计有多少控制权?您可以将Authenticate 更改为表值函数,然后您可以使用IF EXISTS (SELECT 1 FROM Authenticate (@UserName, @Password)。如果您仍然需要其他领域的 SP,您可以将函数命名为其他名称,并让 SP 调用该函数以节省维护两个脚本。

标签: sql-server tsql stored-procedures


【解决方案1】:

您可以将您的过程转换为函数:

create function dbo.authenticate(@UserName varchar(50), @Password varchar(50))
returns @found table(userName varchar(50), userPass varchar(50)) as
begin
  -- some of your internal table
  declare @user table (userName varchar(50), userPass varchar(50));
  insert into @user values ('John', '123'), ('Jack', '345');

  insert into @found (userName, userPass)
    select
      u.userName, u.userPass
    from
      @user u
    where
      u.userName = @UserName and u.userPass = @Password
  ;

  return;

end;

go

if exists(select * from dbo.authenticate('John', '123')) begin
  print 'exists';
end;

【讨论】:

  • 我认为考虑到约束它是更好的解决方案,但我可以返回 @found 表而不定义其列名。我真的很想返回“从用户表中选择用户名和密码匹配的行”的结果。
  • 我使用 SQL Management Studio 中的 CREATE TO 脚本来获取 @found 表列名称,并按预期工作。这样做是可以的,因为您只需在函数内创建一次这些字段,然后在 IF EXISTS 任何我需要的地方使用它。谢谢。
【解决方案2】:

EXISTS 采用 SELECT 子查询,而不是存储过程的结果。见MSDN。您将需要在EXISTS 语句中从authenticate 复制您的SELECT,或者使用EXISTS 之前的结果填充表。例如

INSERT INTO #authenticate (col1, col2, col3)
EXEC authenticate @UserName, @Password

IF EXISTS (SELECT 1 FROM #authenticate)
BEGIN    
    SELECT * from Users;
END

【讨论】:

  • 我想这样做,但我真的不想再次为#authenticate 表添加所有字段。我们是否可以在不指定列名的情况下创建临时表,即它可以以某种方式从存储过程的结果中推断出列名?
  • 你需要使用OPENROWSET:见this old answer
【解决方案3】:

我看到了几个设计问题:

1

方法“Authenticate”返回一些行集。为什么?它被称为“身份验证” - 而不是“SelectAuthenticatedUserInfo”。我认为这个过程不止一项工作。

将其拆分为真正的身份验证 方法,该方法可能会给出一个带有会话ID 或位标志yes/no = ok/fault 的输出变量。并创建另一个类似dbo.SelectAuthUserProfile 的过程,它在应用程序启动时调用一次,从dbo.Authenticate 内部调用,如果成功 - 返回带有相应数据的行集。

2

我的意思是,此选择看起来像是一项常规工作 - 它不太可能是应用启动或登录/退出的一次性“低级”工作。你为什么要执行 authenticate 方法?您是否认真地想要在每次选择某些数据时传递 username 和 (!) password?身份验证是每个会话(或什至更长的场景)的一次性工作。

对您的用户进行身份验证,然后检查他是否已经通过身份验证。将会话数据存储在某个地方,只传递一个密钥或其他东西(甚至不传递任何东西 - ## 或 # 表、spid+登录时间等有一些技巧)。

3

在我看来并不是每个用户都可以查看Users 表的内容。访问可以通过内部 sql-server 的工具来控制。

为特定于管理员的存储过程创建模式,为这些超级用户创建角色,授予他们这个模式的过程。这将排除普通用户执行此类过程的任何可能性。

4

进一步考虑管理对系统不同部分的访问。对于“此用户是否允许执行这样的操作?”的问题,您正在尝试回答“他已通过身份验证!”好的,已通过身份验证。所以呢?他有没有许可?

验证用户一次,然后 - 控制他的权限。常规过程中不能有“身份验证”方法。但我猜应该是“IsPermitted”。 (此项目与 proc 的第一至多职责相关)

5

此外,考虑一下:允许查看这些订单,但不允许查看那些。你能用像if exec then这样的proc调用来安排它吗?

【讨论】:

  • 感谢您的详细建议。为什么来回传递密钥可以,但不能传递用户名和密码?我在进行身份验证时检索用户配置文件,因为然后您将这些信息保存在客户端以进行进一步的调用。为什么要在身份验证后再次调用以获取用户配置文件?我同意为是/否创建一个身份验证函数,但是从客户端我们将不得不调用 SelectAuthUserProfile 并在此过程中使用身份验证,对吗?我们不能对自身使用身份验证来为用户获取任何有用的信息。我们确实有角色表。
  • 你的“角色”表是我所说的 MS SQL SERVER 安全系统的一部分吗?如果在每个 db 调用中使用登录密码验证用户对您来说是可以的,那么我的帖子可能与您的系统类型无关。
猜你喜欢
  • 1970-01-01
  • 2014-01-07
  • 2012-08-08
  • 2012-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-06
  • 1970-01-01
相关资源
最近更新 更多