【问题标题】:SQL: Writing a bulk insert script that ignores existing entriesSQL:编写忽略现有条目的批量插入脚本
【发布时间】:2014-12-01 23:42:12
【问题描述】:

我从一个使用 sql 数据库的现有程序开始,并尝试对其进行修改,以便它使用批量插入(而不是一个接一个)并防止重复条目。

这是我添加一个用户的功能(只要该用户不存在),它应该可以工作:

use [DebugDatabase]
go

set ansi_nulls on
go

set quoted_identifier on
go

create procedure [dbo].[AddUser]
    @Id bigint, @Login nvarchar(100), @ConsoleName nvarchar(100), @Ip nvarchar(50)
as
begin
    set nocount on;

    declare @FoundId bigint = (select Id from [User] where Id = @Id)

    if @FoundId is null
        begin
            insert into [User] (Id, Login, ConsoleName, Ip) values (@Id, @Login, @ConsoleName, @Ip)
        end
    else
        begin
            update [User]
            set [Id] = @Id,
                [Login] = @Login,
                [ConsoleName] = @ConsoleName,
                [Ip] = @Ip
            where Id = @Id
        end
    select top 1 * from [User] where Id = @Id;
end
go

现在我想为批量插入编写一个函数,它调用上述函数来检查每个条目。显示我的方式,这需要自定义表格类型:

use [DebugDatabase]
go

create type [dbo].[UserTableType] as table
    (
    [Id] [bigint] not null,
    [Login] [nvarchar](100) not null default 'Unknown',
    [ConsoleName] [nvarchar](100) not null default 'Unknown',
    [Ip] [nvarchar](50) not null default '0.0.0.0'
    )
go

以及添加多个条目的功能(这是我遇到的问题):

use [DebugDatabase]
go

set ansi_nulls on
go

set quoted_identifier on
go

create procedure [AddMultipleUsers]
    @users UserTableType readonly

as

declare
    @Id bigint,
    @Login nvarchar(100),
    @ConsoleName nvarchar(100),
    @Ip nvarchar(50)

begin
    insert into @Id select Id from @users
    insert into @Login select Login from @users
    insert into @ConsoleName select ConsoleName from @users
    insert into @Ip select Ip from @users

    exec AddUser @Id, @Login, @ConsoleName, @Ip
end
go

我得到“必须声明表变量“@Id”。我不确定如何从表类型中提取单个值以便将它们传递给 AddUser 函数。我真的很喜欢,虽然,是一种在一个函数调用中完成所有操作的方法,但我还没有遇到过这样的事情。

【问题讨论】:

  • 您对“@Id”的插入将不起作用,因为“@Id”是一个 bigint 而不是一个表。您应该像这样设置@Id 参数:'Set @Id= (Select top 1 Id from @users)'

标签: sql stored-procedures bulkinsert


【解决方案1】:

这种类型的操作可以使用单个查询来完成,而无需使用存储过程。 (顺便说一句,请使用正确的术语:您的 AddUser 是一个存储过程,而不是一个函数)。请查看MERGE 命令

MERGE [User] AS target
    USING (SELECT [Id], [Login], [ConsoleName], [Ip] FROM @users) AS source
    (Id, Login, ConsoleName, Ip)
    ON (target.Id = source.Id)
WHEN MATCHED THEN 
        UPDATE SET [Login] = source.Login,
                   [ConsoleName] = source.ConsoleName,
                   [Ip] = source.Ip
WHEN NOT MATCHED THEN
    INSERT (Id, Login, ConsoleName, Ip)
    VALUES (source.Id, source.Login, source.ConsoleName, source.Ip);

【讨论】:

  • 谢谢,到目前为止这似乎运作良好。因为长期目标是使用实体框架从 c# 程序中调用它,所以我认为它确实必须存储为过程。
  • 只需放入 [AddMultipleUsers] 的正文中并删除您的 DECLARE 语句
  • 这正是我所做的。谢谢你的帮助,查。
【解决方案2】:
create procedure [AddMultipleUsers]
  @users UserTableType readonly

as

declare
  @Id bigint,
  @Login nvarchar(100),
  @ConsoleName nvarchar(100),
  @Ip nvarchar(50)

--  A opcion ... 
-- Declare the cursor for the list of users to be processed.
DECLARE userT CURSOR FOR SELECT * FROM UserTableType ;

-- Open the cursor.
OPEN userT;

-- Loop through the users.
FETCH NEXT
  FROM userT
  INTO @Id, @Login, @ConsoleName, @Ip;

WHILE @@FETCH_STATUS = 0
BEGIN;
  exec AddUser @Id, @Login, @ConsoleName, @Ip

  FETCH NEXT FROM userT INTO @Id, @Login, @ConsoleName, @Ip;
END;

-- Close and deallocate the cursor.
CLOSE partitions;
DEALLOCATE partitions;

【讨论】:

    猜你喜欢
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 2012-08-07
    • 2019-09-20
    • 1970-01-01
    • 1970-01-01
    • 2017-03-30
    • 1970-01-01
    相关资源
    最近更新 更多