使用 IF NOT EXISTS 生成插入脚本

Posted

技术标签:

【中文标题】使用 IF NOT EXISTS 生成插入脚本【英文标题】:Generate Insert Scripts with IF NOT EXISTS 【发布时间】:2014-01-14 21:36:17 【问题描述】:

我在数据库中有一个主表。

示例菜单表

+-----------+-----------+-------------+---------+------------------------+
| Id        | Key       | Display Text| ParentId| CreatedOn
+-----------+-----------+-------------+---------+------------------------+
| 1         | Home      | Home        | NULL    |2014-01-14 21:17:37.387 |
| 2         | About     | About Us    | NULL    |2014-01-14 21:17:37.387 |
| 3         | Contact   | Contact Us  | NULL    |2014-01-14 21:17:37.387 |
+-----------+-----------+------+------+---------+------------------------+

我曾经为每条记录生成如下主数据脚本。

IF NOT EXISTS(SELECT 1 FROM [Menu] WHERE Id=1 AND Key='Home')
BEGIN
SET IDENTITY_INSERT [dbo].[Menu] ON

INSERT INTO [dbo].[Menu]
           (Id
           ,[Key]
           ,[DisplayText]
           ,[ParentId]
           ,[CreatedOn])
     VALUES
           (1
           ,'Home'
           ,'Home'
           ,NULL
           ,GETDATE()
           )
SET IDENTITY_INSERT [dbo].[Menu] OFF

END
GO

-- 为所有 70 条记录和其他主数据重复相同的手动记录创建工作(10k rows)

但是,另一个数据库中的一些现有表ApplicationMenu 具有相同的列、数据类型。我们想通过使用一些存储过程为我们自动生成以下脚本。

是否可以创建如下程序

CREATE PROCEDURE spGenerateInsertScripts
(
  @SourceTableName VARCHAR(100),
  @ExistsWhereClauseTemplate NVARCHAR(1000),
  @TargetTableName VARCHAR(100)
)
BEGIN

 -- In some loop create those above insert statements
END

我们想像下面这样执行

  exec spGenerateInsertScripts 'ApplicationMenu'
                               , 'WHERE Id=Id AND Key=Key'
                               , 'Menu'

这里 Id & Key 将从现有表的每一行中读取并替换。

这实际上会为我们减少大量的手工工作。

注意:

我们不能使用 SQL server 插入脚本生成工具,因为我们要检查数据是否存在以及需要保留用户使用我们的应用程序添加的记录。

需要生成一个插入脚本,以便我们以后可以运行,即使 ApplicationTable 不可用

是否可以编写这样的程序来根据存在从其他表生成插入脚本?就像 sql server Generate Scripts work for table creation by looking into INFORMATION_SCHEMA 表一样,我对此的期望也是如此。

过程的最终输出类似于PRINT @insert_Sql_Statements

【问题讨论】:

因此您想使用 ApplicationMenu 表(在其他数据库中)在 Menu 数据库中创建行(如果这些行不存在)。使用 ApplicationMenu 中的 Id 和 Key 列? 为什么不创建一个接受表值参数的存储过程并将值传递给存储过程,然后返回添加到目标表的值。 ?????? @Umair,是的。这两个表都具有相同的列名和数据类型。我只想将所有 ApplicationMenu 导入到我的菜单表中,前提是它们不存在 @M.Ali,我需要一个插入脚本,以便以后可以随时运行,即使 ApplicationMenu 表不可用.. 【参考方案1】:

您的数据

DECLARE @Table TABLE(Id INT, [Key] VARCHAR(30),[Display Text] VARCHAR(30), ParentId INT,  CreatedOn DATETIME)
INSERT INTO @Table VALUES 
(1,'Home'   ,'Home'      ,NULL, '2014-01-14 21:17:37.387'), 
(2,'About'  ,'About Us'  ,NULL, '2014-01-14 21:17:37.387'),
(3,'Contact','Contact Us',NULL, '2014-01-14 21:17:37.387')

查询创建脚本

SELECT  N'IF NOT EXISTS(SELECT 1 FROM [Menu] WHERE Id='+  CAST(Id AS NVARCHAR(10)) 
       + ' AND Key='''+ CAST([Key] AS NVARCHAR(1000)) +''')' + CHAR(10) 
        + N'BEGIN ' + CHAR(10) + '
        SET IDENTITY_INSERT [dbo].[Menu] ON ' + CHAR(10) + ' 

        INSERT INTO [dbo].[Menu]      ' + CHAR(10) + ' 
                   (Id    ' + CHAR(10) + ' 
                   ,[Key] ' + CHAR(10) + ' 
                   ,[DisplayText]' + CHAR(10) + ' 
                   ,[ParentId]' + CHAR(10) + ' 
                   ,[CreatedOn])' + CHAR(10) + ' 
             VALUES' + CHAR(10) + ' 
                   ( '  + ISNULL(CAST(Id AS NVARCHAR(10)), 'NULL') + ' ' + CHAR(10) + ' 
                   ,''' + ISNULL(CAST([Key] AS NVARCHAR(1000)), 'NULL') +''' ' + CHAR(10) + ' 
                   ,''' + ISNULL(CAST([Display Text] AS NVARCHAR(1000)), 'NULL')  + ''' ' + CHAR(10) + ' 
                   ,' +  ISNULL(CAST(ParentId AS NVARCHAR(10)), 'NULL')  + ' ' + CHAR(10) + ' 
                   ,GETDATE() ' + CHAR(10) + ' 
                   ) ' + CHAR(10) + ' 
        SET IDENTITY_INSERT [dbo].[Menu] OFF ' + CHAR(10) + ' 
        END ' + CHAR(10) + ' 
        GO ' + CHAR(10) + ' '+ CHAR(10)
FROM @Table

生成的脚本

╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║                                                                                                                                                                                                                          (No column name)                                                                                                                                                                                                                          ║
╠════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ IF NOT EXISTS(SELECT 1 FROM [Menu] WHERE Id=1 AND Key='Home') BEGIN      SET IDENTITY_INSERT [dbo].[Menu] ON         INSERT INTO [dbo].[Menu]                 (Id               ,[Key]            ,[DisplayText]           ,[ParentId]           ,[CreatedOn])        VALUES           ( 1            ,'Home'            ,'Home'            ,NULL            ,GETDATE()            )       SET IDENTITY_INSERT [dbo].[Menu] OFF       END       GO                 ║
║ IF NOT EXISTS(SELECT 1 FROM [Menu] WHERE Id=2 AND Key='About') BEGIN      SET IDENTITY_INSERT [dbo].[Menu] ON         INSERT INTO [dbo].[Menu]                 (Id               ,[Key]            ,[DisplayText]           ,[ParentId]           ,[CreatedOn])        VALUES           ( 2            ,'About'            ,'About Us'            ,NULL            ,GETDATE()            )       SET IDENTITY_INSERT [dbo].[Menu] OFF       END       GO           ║
║ IF NOT EXISTS(SELECT 1 FROM [Menu] WHERE Id=3 AND Key='Contact') BEGIN      SET IDENTITY_INSERT [dbo].[Menu] ON         INSERT INTO [dbo].[Menu]                 (Id               ,[Key]            ,[DisplayText]           ,[ParentId]           ,[CreatedOn])        VALUES           ( 3            ,'Contact'            ,'Contact Us'            ,NULL            ,GETDATE()            )       SET IDENTITY_INSERT [dbo].[Menu] OFF       END       GO     ║
╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

注意

我在Grid 中返回了结果,但是您可以将结果导出到文件或文本中,并在您想要执行时将其复制粘贴到您的查询窗口中。

【讨论】:

Awwww,基本相同的答案慢了 24 分钟。 +1 先生,您先到那里。【参考方案2】:

假设我正确理解了您的问题,您提出的建议(将 where 子句作为参数)听起来不太好,并且可能导致很多其他问题(例如 SQL 注入、验证 SQL 字符串的格式是否正确, ETC)。

这种使用链接服务器的方法怎么样

SET IDENTITY_INSERT [dbo].[Menu] ON
GO

INSERT INTO [dbo].[Menu] ([Id],[Key],[DisplayText],[ParentId],[CreatedOn])
SELECT a.Id, a.Key, a.Key, NULL, GETDATE()
FROM [ApplicationMenu_Instance].[ApplicationMenu_Database].[dbo].[ApplicationMenu] AS a
WHERE NOT EXISTS (
    SELECT 1
    FROM [dbo].[Menu] AS m
    WHERE m.Id = a.Id
        AND m.Key = a.Key
)

SET IDENTITY_INSERT [dbo].[Menu] OFF
GO

更新: 既然要返回插入脚本,那动态SQL怎么样:

CREATE PROCEDURE spGenerateInsertScripts
(
    @SourceTable VARCHAR(100),
    @TargetTable VARCHAR(100)
)  
BEGIN
    DECLARE @SQL NVARCHAR(MAX) = '
    SET IDENTITY_INSERT [dbo].[Menu] ON
    GO

    INSERT INTO [dbo].[' + @TargetTable + '] ([Id],[Key],[DisplayText],[ParentId],[CreatedOn])
    SELECT a.Id, a.Key, a.Key, NULL, GETDATE()
    FROM ' + @SourceTable + '  AS a
    WHERE NOT EXISTS (
        SELECT 1
        FROM [dbo].[' + @TargetTable + '] AS m
        WHERE m.Id = a.Id
            AND m.Key = a.Key
    )

    SET IDENTITY_INSERT [dbo].[Menu] OFF
    GO
   ';

    SELECT @SQL;
END

【讨论】:

但我需要它作为TEXT SCRIPT,这样即使应用程序菜单不可用,我也可以随时运行它。我想在 ApplicationMenu 可用时生成脚本文本,之后我想随时重新运行。 对不起,我不太明白。为什么你不能用上面的代码创建一个 sp,然后在你想的时候执行它? 您是否想要它以便您可以决定源应用程序菜单表的位置(通过传入参数)? 我正在寻找类似于借助 sql server Generate Scripts 工具生成存储过程脚本的方式。当数据库中不存在表时,您可以选择创建表。因此,它会通过检查INFORMATION_SCHEMA 表自动为我们生成一个表脚本。同样的方式 我想要价值声明而不是SELECT a.Id, a.Key, a.Key, NULL, GETDATE() FROM ApplicationMenu。请检查我的问题。截至目前,我正在一一插入。我想要一些帮助程序通过读取另一个表来生成这些记录..【参考方案3】:

您可以使用 SQL 语句来生成所需的插入语句。然后,您可以将输出复制并粘贴到要执行查询的任何位置。

创建从另一个表生成插入语句到一个表的脚本不是通用解决方案,但它会大大减少您的特定情况所需的手动工作。您可以配置目标表的名称,但列名和值以及从中检索数据的表的名称是硬编码的。

假定输入的目标表与从中检索数据的表具有相同的架构。

DECLARE @TARGET_TABLE AS VARCHAR(100) = '[dbo].[Menu]'

SELECT Script
FROM
(
    SELECT Id, [Key], 0 AS [Order], 
        'IF NOT EXISTS(SELECT 1 FROM ' + @TARGET_TABLE + 
        ' WHERE Id=' + CONVERT(varchar(100), Id) + 
        ' AND Key=''' + [Key] + ''')' AS Script
    FROM ApplicationMenu
    UNION
    SELECT Id, [Key], 1 AS [Order], 'BEGIN' AS Script
    FROM ApplicationMenu
    UNION
    SELECT Id, [Key], 2, 'SET IDENTITY_INSERT ' + @TARGET_TABLE + ' ON'
    FROM ApplicationMenu
    UNION
    SELECT Id, [Key], 3, 
        'INSERT INTO ' + @TARGET_TABLE + 
        ' VALUES(' + 
        CONVERT(varchar(11), Id) + ', ''' + 
        [Key] + ''', ''' + 
        [DisplayText] + ''', ' + 
        ISNULL(CONVERT(varchar(11), ParentId), 'NULL') + 
        ', GETDATE())'
    FROM ApplicationMenu
    UNION
    SELECT Id, [Key], 4, 'SET IDENTITY_INSERT ' + @TARGET_TABLE + ' OFF'
    FROM ApplicationMenu
    UNION
    SELECT Id, [Key], 5, 'END'
    FROM ApplicationMenu
) AS ScriptInfo
ORDER BY Id, [Key], [Order]

老实说,这个脚本看起来有点痛苦,但它完成了工作。

如果你真的想要一个通用的解决方案来解决这个问题,你可能会更幸运地用某种编程语言(如 C#)来实现它。在 C# 中实现它的好处是,您可以将库导入 SQL Server 并像存储过程一样调用它(我想,我以前从未做过这种事情)。

此外,还有一些工具可以为您生成此类脚本。如果我没记错的话,RedGate SQL Data Compare 会相当容易地完成这类事情。可能还有其他人。

【讨论】:

以上是关于使用 IF NOT EXISTS 生成插入脚本的主要内容,如果未能解决你的问题,请参考以下文章

if--else 嵌套 怎么理解?

对于互斥条件,多个“if”语句和“if-else-if”语句是不是相同?

html 基本IF,IF - ELSE和IF - ELSE IF - ELSE语句。

利用if函数计算个人所得税

java,if else和if else if else区别?

R语言中的if else语句