【问题标题】:Check if SELECT Returns Any Rows in Stored Procedure检查 SELECT 是否返回存储过程中的任何行
【发布时间】:2013-10-10 16:56:22
【问题描述】:

我正在编写一个基本的SELECT 查询,类似于:

SELECT id, pname, pnumber 
FROM tableName 
WHERE pnumber IS NOT NULL

然后我想通过使用SELECT 的结果来执行INSERT,如下所示:

IF {**the above SELECT query returned 0 rows**}
BEGIN
    INSERT INTO tableName (pname,pnumber) VALUES ('bob', '38499483')
END

我的问题是,如何查看**the above SELECT query returned 0 rows**

【问题讨论】:

  • 这个检查的目的是什么?如果您正在检查唯一性,则列上的唯一索引更简单

标签: sql sql-server stored-procedures


【解决方案1】:
IF NOT EXISTS (SELECT ...)
BEGIN
  INSERT ...
END

如果您预计查询可能经常返回行(尤其是很多行),您也可以这样做,这可能会提供更好的短路机会:

IF EXISTS (SELECT ...)
BEGIN
  PRINT 'Do nothing.';
END
ELSE
BEGIN
  INSERT ...
END

...因为IF EXISTS 将在匹配的第一行后立即返回。

我不建议仅使用@@ROWCOUNT,因为您每次都必须实现(并忽略)完整的结果集。

【讨论】:

  • 我喜欢这个。通常,低技术解决方案是好的,因为它们不是特定于环境的。 SQL 代码经常从 SQL Server 移植到 MySQL 再到 Oracle 等......
  • 在 MySQL 5.7.15 中拒绝这样做:看起来 IF EXISTS (SELECT ...) 在尝试保存存储过程时会导致永久性语法错误。实际上返回: BEGIN 附近的语法错误...在 MySQL 存储过程中 IF 语句需要 IF() THEN ... ELSE ... END IF;不是吗?
  • @PowerEngineering 问题不在于 MySQL,而在于 Microsoft SQL Server。这是你投反对票的困惑吗?
【解决方案2】:

在 MySQL 中,您可以像这样检查上次 SELECT 查询返回的行数:

SELECT FOUND_ROWS();

【讨论】:

  • 很好,在很多情况下这是一个更好的解决方案。就我而言,我不仅想检查该项目是否存在,还想选择它并将其用于以后的一些操作。
【解决方案3】:

你可以使用@@ROWCOUNT

SELECT id, pname, pnumber 
FROM tableName 
WHERE pnumber IS NOT NULL

选择后询问@@ROWCOUNT

IF @@ROWCOUNT = 0
BEGIN
  INSERT ...
END

通过这种方式,您可以返回一些数据并检查是否有结果

【讨论】:

    【解决方案4】:

    代码是:

    EXEC(SELECT * FROM Table_Name)
        IF (@@ROWCOUNT>1)    --@@ROWCOUNT is the count number of return's rows
        BEGIN
           // code here
        END
    

    【讨论】:

      【解决方案5】:

      我看到人们对这种逻辑存在设计模式问题。

      1 - 测试记录的存在性。
      2 - 如果不存在,则插入记录。

      尤其是在并发发挥作用的情况下。根据隔离级别,您可能有重复数据或密钥违规。

      为什么不首先在 pname 和 pnumber 上放置一个主键。我假设你在谈论一个人表。

      我的例子。

      --
      -- Setup sample table w/data
      --
      
      -- Sample table
      create table #person
      ( 
          person_id int identity (1, 1),
          person_name varchar(64) not null,
          person_no varchar(16) not null
      );
      go
      
      -- primary key
      alter table #person 
         add primary key (person_no, person_name)
      go
      
      -- first insert works
      insert into #person (person_name, person_no) values ('bilbo', 123)
      go
      

      解决的关键是捕获主键违规。根据您的业务逻辑,忽略此错误可能是好是坏。

      我决定忽略这个问题。

      -- 
      -- Ignore primary key violations
      --
      
      -- Try these steps
      BEGIN TRY
      
          -- Second insert fails
          insert into #person (person_name, person_no) values ('bilbo', 123)
      
      END TRY
      
      -- Error Handler
      BEGIN CATCH
      
          -- Ignore PK error
          IF ERROR_NUMBER() <> 2627
          SELECT
              ERROR_NUMBER() AS ErrorNumber
             ,ERROR_SEVERITY() AS ErrorSeverity
             ,ERROR_STATE() AS ErrorState
             ,ERROR_PROCEDURE() AS ErrorProcedure
             ,ERROR_LINE() AS ErrorLine
             ,ERROR_MESSAGE() AS ErrorMessage;
      END CATCH
      

      此解决方案消除了重复条目,并且不报告 PK 违规。

      【讨论】:

      • 我试图只在不存在的情况下执行 INSERT,因为我在 ASP.NET 中遇到了回发问题。所以存在的记录意味着它不需要被插入,因为它已经被记录了。话虽如此,我将在即将进行的设计更改中考虑您的回答。谢谢。
      • 我的一台服务器上有一个 Microsoft 产品,它执行相同的逻辑。如果存在,则返回密钥,否则,插入更新锁。我经常在我的 SQL Sentry 监控系统中收到死锁通知。我提出的解决方案是上面的,但是他们还没有实现!
      猜你喜欢
      • 2013-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-01
      • 2021-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多