检查SQL Server中是否存在表


1142

我希望这是关于如何使用SQL语句检查SQL Server 2000/2005中是否存在表的最终讨论。

当您用Google搜索答案时,会得到很多不同的答案。有官方/后向和向前兼容的方式吗?

这是两种可能的方法。两种方法中的哪一种是标准/最佳方法?

第一种方式:

IF EXISTS (SELECT 1 
           FROM INFORMATION_SCHEMA.TABLES 
           WHERE TABLE_TYPE='BASE TABLE' 
           AND TABLE_NAME='mytablename') 
   SELECT 1 AS res ELSE SELECT 0 AS res;

第二种方式:

IF OBJECT_ID (N'mytablename', N'U') IS NOT NULL 
   SELECT 1 AS res ELSE SELECT 0 AS res;

MySQL提供的简单

SHOW TABLES LIKE '%tablename%'; 

声明。我正在寻找类似的东西。


1
为什么最好使用INFORMATION_SCHEMA.TABLES而不是sys.tables,然后使用名称过滤掉任何名称,也许添加type_desc值检查?
DanteTheSmith's

Answers:


1331

对于此类查询,最好始终使用INFORMATION_SCHEMA视图。这些视图(大多数情况下)是跨许多不同数据库的标准视图,并且很少因版本而异。

要检查表是否存在,请使用:

IF (EXISTS (SELECT * 
                 FROM INFORMATION_SCHEMA.TABLES 
                 WHERE TABLE_SCHEMA = 'TheSchema' 
                 AND  TABLE_NAME = 'TheTable'))
BEGIN
    --Do Stuff
END

12
很棒!但是,在T-SQL中(响应原始消息),它是TABLE_SCHEMA,而不是SCHEMA_NAME。谢谢你的提示。
尼古拉斯·皮亚塞斯基

10
鉴于不能保证单独的对象名称(即没有模式)是唯一的,因此没有100%的故障保护方法可以做到这一点。如果您使用的数据库之间没有命名冲突,那么只需省略“ TABLE_SCHEMA ='TheSchema'”就可以了。
akmad

26
要检查临时表,我们必须查询tempdb数据库并使用LIKE运算符作为表名SELECT * FROM tempdb.INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'TheSchema' AND TABLE_NAME LIKE '#TheTable%'
Pierre-Alain Vigeant 2010年

4
下面是A响应使用OBJECT_ID功能不正确地操作每个连接临时表关于- stackoverflow.com/a/2155299/16147
富卢梭

4
@akmad SQL查询编译器将在运行查询之前检查表,并且甚至在启动之前都会失败。
Marc K

278

另请注意,如果出于任何原因需要检查临时表,可以执行以下操作:

if OBJECT_ID('tempdb..#test') is not null
 --- temp table exists

10
使用此方法似乎尊重临时表的每个连接性质。较早发布的INFORMATION_SCHEMA查询将返回行,而不管创建表的连接如何。
Rich Rousseau 2012年

238

OBJECT_ID只要我记得,我们就一直使用该样式

IF OBJECT_ID('*objectName*', 'U') IS NOT NULL 

16
我相信这会很快,尽管不是很方便。保证信息模式视图存在于任何支持该标准的DBRMS上。此外,普通的OBJECT_ID不能保证对象是表。
乔·派恩达

9
谢谢Joe,我想知道为什么要使用OBJECT_ID与INFORMATION_SCHEMA.TABLES与sys.tables。指出INFORMATION_SCHEMA是标准的一部分,几乎可以回答该问题。顺便说一句,很有趣,我要问这个问题的一位数据库专家与您的姓氏相同,必须是数据库的好姓氏。
Apeiron

24
@JoePineda:然后用例OBJECT_ID('TableName', 'U')确保对象是一个表。
Allon Guralnek,2011年

1
@AllonGuralnek,那么,不是遵循简单且可移植的标准,而是添加额外的一条神秘信息吗?
定义

22
@DustinFineout:这个问题被标记为tsql,所以可移植性不太适用。总的来说,我遇到的真正可移植代码库很少,如果重视简洁性,那肯定IF EXISTS比一遍又一遍地从接受的答案中编写该查询要好。另外,在您阅读文档之前,所有内容都是神秘的,尤其是在T-SQL(或其他任何变体)中。
Allon Guralnek'5

132

请查看以下方法,

方法1:使用INFORMATION_SCHEMA.TABLES视图

我们可以编写如下查询,以检查当前数据库中是否存在客户表。

IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'Customers')
BEGIN
    PRINT 'Table Exists'
END

方法2:使用OBJECT_ID()函数

我们可以使用如下所示的OBJECT_ID()函数来检查当前数据库中是否存在客户表。

IF OBJECT_ID(N'dbo.Customers', N'U') IS NOT NULL
BEGIN
    PRINT 'Table Exists'
END

方法3:使用sys.Objects目录视图

我们可以使用Sys.Objects目录视图来检查表的存在,如下所示:

IF EXISTS(SELECT 1 FROM sys.Objects WHERE  Object_id = OBJECT_ID(N'dbo.Customers') AND Type = N'U')
BEGIN
   PRINT 'Table Exists'
END

方法4:使用sys.tables目录视图

我们可以使用Sys.Tables目录视图来检查Table的存在,如下所示:

 IF EXISTS(SELECT 1 FROM sys.Tables WHERE  Name = N'Customers' AND Type = N'U')
 BEGIN
      PRINT 'Table Exists'
 END

方法5:避免使用sys.sysobjects系统表

我们应该避免直接使用sys.sysobjects系统表,在某些将来的Sql Server版本中将不建议直接使用它。根据Microsoft BOL链接,Microsoft建议直接使用目录视图sys.objects / sys.tables代替sys.sysobjects系统表。

  IF EXISTS(SELECT name FROM sys.sysobjects WHERE Name = N'Customers' AND xtype = N'U')
  BEGIN
     PRINT 'Table Exists'
  END

引用自:http : //sqlhints.com/2014/04/13/how-to-check-if-a-table-exists-in-sql-server/



26

只是想提及一种情况,使用该OBJECT_ID方法可能会更容易一些。这些INFORMATION_SCHEMA视图是每个数据库下的对象-

信息模式视图是在名为INFORMATION_SCHEMA的特殊模式中定义的。该模式包含在每个数据库中。

https://msdn.microsoft.com/zh-CN/library/ms186778.aspx

因此,您使用来访问的所有表

IF EXISTS (SELECT 1 
           FROM [database].INFORMATION_SCHEMA.TABLES 
           WHERE TABLE_TYPE='BASE TABLE' 
           AND TABLE_NAME='mytablename') 
   SELECT 1 AS res ELSE SELECT 0 AS res;

只会反映中的内容[database]。如果要检查另一个数据库中的表是否存在,而不必[database]每次都进行动态更改,OBJECT_ID则可以立即进行操作。前

IF OBJECT_ID (N'db1.schema.table1', N'U') IS NOT NULL 
   SELECT 1 AS res ELSE SELECT 0 AS res;

效果和

IF OBJECT_ID (N'db2.schema.table1', N'U') IS NOT NULL 
   SELECT 1 AS res ELSE SELECT 0 AS res;

SQL SERVER 2016编辑

从2016年开始,Microsoft通过将if exists关键字添加到drop语句中,简化了删除之前不存在对象的检查功能。例如,

drop table if exists mytablename

在1行代码中将执行与OBJECT_ID/ INFORMATION_SCHEMA包装器相同的操作。

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/




11
IF EXISTS 
(
    SELECT   * 
    FROM     sys.objects 
    WHERE    object_id = OBJECT_ID(N'[dbo].[Mapping_APCToFANavigator]') 
             AND 
             type in (N'U')
)
BEGIN

    -- Do whatever you need to here.

END

在上面的代码中,表名是Mapping_APCToFANavigator


2
如果发布代码,XML或数据示例,在文本编辑器中突出显示这些行,然后单击{ }编辑器工具栏上的“代码示例”按钮(),以很好地格式化和语法突出显示它!
marc_s

1
请注意,在将来的SQL Server版本中,可能会停止对系统表的访问。请改用架构视图。
Olivier Jacot-Descombes 2014年

10

如果您需要在其他数据库上工作:

DECLARE @Catalog VARCHAR(255)
SET @Catalog = 'MyDatabase'

DECLARE @Schema VARCHAR(255)
SET @Schema = 'dbo'

DECLARE @Table VARCHAR(255)
SET @Table = 'MyTable'

IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES   
    WHERE TABLE_CATALOG = @Catalog 
      AND TABLE_SCHEMA = @Schema 
      AND TABLE_NAME = @Table))
BEGIN
   --do stuff
END

1
你确定吗?我的2005框上的信息架构仅返回当前目录。
quillbreaker 2013年

8

我知道这是一个老问题,但是如果您打算经常调用它,我已经发现了这种可能性。

create procedure Table_Exists
@tbl varchar(50)
as
return (select count(*) from sysobjects where type = 'U' and name = @tbl)
go

9
-1。毫无意义的有一个过程,因为调用和使用返回代码就像执行选择一样多。应该不使用sysname数据类型varchar(50)。不应使用已弃用的sysobjects视图,也不考虑架构。
马丁·史密斯,

6

您可以使用以下代码

IF (OBJECT_ID('TableName') IS NOT NULL )
BEGIN
  PRINT 'Table Exists'
END
ELSE
BEGIN 
  PRINT 'Table NOT Exists'
END

要么

IF (EXISTS (SELECT * FROM sys.tables WHERE [name] = 'TableName'))
BEGIN
  PRINT 'Table Exists'
END
ELSE
BEGIN 
  PRINT 'Table NOT Exists'
END

5

仅在此处添加内容,以使开发人员和其他DBA受益

接收@Tablename作为参数的脚本

(可能包含也可能不包含模式名称),如果schema.table存在,则返回以下信息:

the_name                object_id   the_schema  the_table       the_type
[Facts].[FactBackOrder] 758293761   Facts       FactBackOrder   Table

每次需要测试表或视图是否存在时,我都会生成此脚本以在其他脚本中使用,并且当它存在时,请获取其object_id以用于其他用途。

当您传递空字符串,错误的架构名称或错误的表名称时,都会引发错误。

这可以在过程内部,例如返回-1。

例如,我的一个数据仓库数据库中有一个名为“ Facts.FactBackOrder”的表。

这就是我实现这一目标的方式:

PRINT 'THE SERVER IS ' + @@SERVERNAME
--select db_name()
PRINT 'THE DATABASE IS ' + db_NAME() 
PRINT ''
GO

SET NOCOUNT ON
GO

--===================================================================================
-- @TableName is the parameter
-- the object we want to deal with (it might be an indexed view or a table)
-- the schema might or might not be specified
-- when not specified it is DBO
--===================================================================================

DECLARE @TableName SYSNAME

SELECT @TableName = 'Facts.FactBackOrder'
--===================================================================================
--===================================================================================
DECLARE @Schema SYSNAME
DECLARE @I INT
DECLARE @Z INT 

SELECT @TableName = LTRIM(RTRIM(@TableName))
SELECT @Z = LEN(@TableName)

IF (@Z = 0) BEGIN

            RAISERROR('Invalid @Tablename passed.',16,1)

END 

SELECT @I = CHARINDEX('.',@TableName )
--SELECT @TableName ,@I

IF @I > 0 BEGIN

        --===================================================================================
        -- a schema and table name have been passed
        -- example Facts.FactBackOrder 
        -- @Schema = Fact
        -- @TableName = FactBackOrder
        --===================================================================================

   SELECT @Schema    = SUBSTRING(@TABLENAME,1,@I-1)
   SELECT @TableName = SUBSTRING(@TABLENAME,@I+1,@Z-@I)



END
ELSE BEGIN

        --===================================================================================
        -- just a table name have been passed
        -- so the schema will be dbo
        -- example Orders
        -- @Schema = dbo
        -- @TableName = Orders
        --===================================================================================

   SELECT @Schema    = 'DBO'     


END

        --===================================================================================
        -- Check whether the @SchemaName is valid in the current database
        --===================================================================================

IF NOT EXISTS ( SELECT * FROM INFORMATION_SCHEMA.SCHEMATA K WHERE K.[SCHEMA_NAME] = @Schema ) BEGIN

            RAISERROR('Invalid Schema Name.',16,1)

END 

--SELECT @Schema  as [@Schema]
--      ,@TableName as [@TableName]


DECLARE @R1 TABLE (

   THE_NAME SYSNAME
  ,THE_SCHEMA SYSNAME
  ,THE_TABLE SYSNAME
  ,OBJECT_ID INT
  ,THE_TYPE SYSNAME
  ,PRIMARY KEY CLUSTERED (THE_SCHEMA,THE_NAME)

)

;WITH RADHE_01 AS (
SELECT QUOTENAME(SCHEMA_NAME(O.schema_id)) + '.' + QUOTENAME(O.NAME) AS [the_name]
      ,the_schema=SCHEMA_NAME(O.schema_id)
      ,the_table=O.NAME
      ,object_id =o.object_id 
      ,[the_type]= CASE WHEN O.TYPE = 'U' THEN 'Table' ELSE 'View' END 
from sys.objects O
where O.is_ms_shipped = 0
AND O.TYPE IN ('U','V')
)
INSERT INTO @R1 (
   THE_NAME 
  ,THE_SCHEMA 
  ,THE_TABLE 
  ,OBJECT_ID
  ,THE_TYPE 
)
SELECT  the_name
       ,the_schema
       ,the_table
       ,object_id
       ,the_type
FROM RADHE_01
WHERE the_schema = @Schema 
  AND the_table  = @TableName

IF (@@ROWCOUNT = 0) BEGIN 

             RAISERROR('Invalid Table Name.',16,1)

END 
ELSE BEGIN

    SELECT     THE_NAME 
              ,THE_SCHEMA 
              ,THE_TABLE 
              ,OBJECT_ID
              ,THE_TYPE 

    FROM @R1

END 

您的脚本中有很多假设。例如,我可以很容易地创建一个名为的表dbo.[hello.world ],而脚本由于多种原因而找不到它。就是说,不太可能有人会创建这样的表,但是仍然如此。无论如何,您的THE_NAME列被定义为sysname', yet you try to squeeze 2 sysname`列和一个点(.),所有这些都被括在方括号内...一定有一天会失败!
deroby

@deroby我同意sysname并不是要使用的最佳数据类型,但是该脚本已经运行了很长时间,没有任何错误,只有在找到合理的情况下它才会起作用,我才会花一些时间。更好的是,您将这段代码加以改进,并将其发布在此处作为答案,我将对其进行测试,如果它可以工作,我将对您的答案进行投票。
Marcello Miorelli,2013年

5

SQL Server 2000中,您可以尝试:

IF EXISTS(SELECT 1 FROM sysobjects WHERE type = 'U' and name = 'MYTABLENAME')
BEGIN
   SELECT 1 AS 'res' 
END

3
IF EXISTS 
(
    SELECT  * 

    FROM    INFORMATION_SCHEMA.TABLES 

    WHERE   TABLE_SCHEMA = 'PutSchemaHere'     
            AND  
            TABLE_NAME   = 'PutTableNameHere'
)

2

对于尚未找到解决方案的任何人来说,要知道的重要信息: SQL server!= MYSQL。如果要使用MYSQL进行操作,这非常简单

    $sql = "SELECT 1 FROM `db_name`.`table_name` LIMIT 1;";
    $result = mysql_query($sql);
    if( $result == false )
        echo "table DOES NOT EXIST";
    else
        echo "table exists";

将其发布在这里,因为它是Google的热门话题。


4
-1是因为OP恰好要求SQL Server解决该问题,而不是MySQL。他之所以写MySQL是因为他知道该DBMS上的解决方案,并且希望在SQL Server上获得相同的结果。另外,您的答案甚至不是MySQL查询,而是与MySQL一起使用的PHP代码。
mordack550 2014年

1
@ mordack550,我同意布劳恩。他是对的。这是Google查明SQL中是否存在表的最佳选择。他的意图很好,他的信息很有帮助。+1
2014年

不幸的是,Microsoft习惯于尝试拥抱/扩展/熄灭诸如SQL之类的通用标准和模式。我真的希望他们给SQL Server的实现起一个适当的名称,以便人们可以明确地标识特定于其产品的引用。
psaxton


2

您可以使用:

     IF OBJECT_ID (N'dbo.T', N'U') IS NOT NULL 
        BEGIN 
            print 'deleted table';
            drop table t 
        END
     else 
        begin 
            print 'table not found' 
        end

 Create table t (id int identity(1,1) not null, name varchar(30) not null, lastname varchar(25) null)
 insert into t( name, lastname) values('john','doe');
 insert into t( name, lastname) values('rose',NULL);

 Select * from t
1   john    doe
2   rose    NULL

 -- clean
 drop table t


1

--创建过程以检查表是否存在


DELIMITER $$

DROP PROCEDURE IF EXISTS `checkIfTableExists`;

CREATE PROCEDURE checkIfTableExists(
    IN databaseName CHAR(255),
    IN tableName CHAR(255),
    OUT boolExistsOrNot CHAR(40)
)

  BEGIN
      SELECT count(*) INTO boolExistsOrNot FROM information_schema.TABLES
      WHERE (TABLE_SCHEMA = databaseName)
      AND (TABLE_NAME = tableName);
  END $$

DELIMITER ;

--使用方法:检查表迁移是否存在


 CALL checkIfTableExists('muDbName', 'migrations', @output);


1

我以这里创建一个视图为例

因为ALTER / CREATE命令不能在BEGIN / END块内。在创建之前,您需要测试是否存在并删除它

IF Object_ID('TestView') IS NOT NULL
DROP VIEW TestView

GO

CREATE VIEW TestView
   as
   . . .

GO

如果您担心丢失的权限,也可以编写GRANT语句的脚本,然后在末尾重新运行。

您可以将创建/更改包装到字符串中并执行EXEC-对于大视图而言可能很难看

DECLARE @SQL as varchar(4000)

-- set to body of view
SET @SQL = 'SELECT X, Y, Z FROM TABLE' 

IF Object_ID('TestView') IS NULL
    SET @SQL = 'CREATE VIEW TestView AS ' + @SQL
ELSE    
    SET @SQL = 'ALTER VIEW TestView AS ' + @SQL

0

如果有人试图在linq to sql(特别是linqpad)中执行相同的操作,请打开选项以包括系统表和视图,并执行以下代码:

let oSchema = sys.Schemas.FirstOrDefault(s=>s.Name==a.schema )
where oSchema !=null
let o=oSchema!=null?sys.Objects.FirstOrDefault (o => o.Name==a.item && o.Schema_id==oSchema.Schema_id):null
where o!=null

假设您有一个对象,该对象的名称在名为item的属性中,并且该模式在一个名为schema的属性中,其中源变量名称为 a


0

如果这是“最终”讨论,则应注意,如果服务器已链接,则Larry Leonard的脚本也可以查询远程服务器。

if exists (select * from REMOTE_SERVER.MyOtherDatabase.sys.tables where name = 'MyTable')
    print 'Exists'

也许更适合将其添加为伦纳德答案的注释,或者作为编辑?
EWit 2014年

0

从INFORMATIONAL_SCHEME和OBJECT_ID中选择时,我遇到了一些问题。我不知道这是ODBC驱动程序还是其他问题。.来自SQL Management Studio的查询都可以。

解决方法如下:

SELECT COUNT(*) FROM <yourTableNameHere>

因此,如果查询失败,则数据库中可能没有这样的表(或者您没有对此表的访问权限)。

通过比较处理ODBC驱动程序的SQL执行程序返回的值(在我的情况下为整数)来完成检查。

if (sqlexec(conectionHandle, 'SELECT COUNT(*) FROM myTable') == -1) {
  // myTable doesn't exist..
}

输出什么失败?
wscourge

@ wscourge,SQL查询失败或类似。我只是检查执行程序函数的返回值。
Michael Quad

将其添加到您的答案中
wscourge

0

还有一个选项可以检查表是否存在于数据库中

IF EXISTS(SELECT 1 FROM [change-to-your-database].SYS.TABLES WHERE NAME = 'change-to-your-table-name')
BEGIN
    -- do whatever you want
END

-1

运行此查询以检查表是否存在于数据库中:

IF(SELECT TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'YourTableName') IS NOT NULL
PRINT 'Table Exists';

-6

考虑在一个数据库中您有一个表t1。您想在其他数据库上运行脚本,例如-如果t1存在,则不执行其他任何操作来创建t1。为此,请打开Visual Studio,然后执行以下操作:

右键单击t1,然后单击“脚本表”,然后单击“ DROP”和“创建到”,然后单击“新建查询编辑器”。

您将找到所需的查询。但是在执行该脚本之前,不要忘记注释掉查询中的drop语句,因为如果已有一个,则不想创建新的语句。

谢谢


在SSMS 2012中,它不再执行if-exists检查(如果有)(我不记得以前的版本是如何生成上述脚本的)。也许您对其他数据库工具编写表对象脚本的方式感到误解?
Ivaylo Slavov

如果您要求,SSMS将执行if-exists检查。工具>选项> SQL Server对象资源管理器>脚本>“对象脚本选项”:“检查对象是否存在” = True
Seann Alexander
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.