【问题标题】:Database only updating first row with diffrent value C#数据库仅更新具有不同值 C# 的第一行
【发布时间】:2014-09-15 16:18:32
【问题描述】:
        string connetionString = null;
        SqlConnection connection;
        SqlCommand command;
        SqlDataAdapter adpter = new SqlDataAdapter();
        DataSet ds = new DataSet();
        XmlReader xmlFile;
        string sql = null;

        int ID = 0;
        string Name = null, Text = null, Screenname = null;


        connetionString = "myconnection";

        connection = new SqlConnection(connetionString);

        xmlFile = XmlReader.Create("my.XML", new XmlReaderSettings());
        ds.ReadXml(xmlFile);
        int i = 0;
        connection.Open();
        for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
        {
            ID = Convert.ToInt32(ds.Tables[0].Rows[i].ItemArray[0]);
            Text = ds.Tables[0].Rows[i].ItemArray[1].ToString().Replace("'", "''");
            Name = ds.Tables[0].Rows[i].ItemArray[2].ToString().Replace("'", "''");
            Screenname = ds.Tables[0].Rows[i].ItemArray[3].ToString().Replace("'", "''");

            //sql = "insert into nicktest values(" + ID + ",'" + Text + "'," + Name + "," + Screenname + "," + DateTime.Now.ToString() + ")";
            sql = "If Exists(Select * from niktest2 Where Id  = ID) " +
                                        " BEGIN " +
                                        " update niktest2 set Name  = '" + Text + "' , Screenname = '" + Name + "', Profimg= '" + Screenname + "', InsertDateTime= '" + DateTime.Now.ToString() + "' where Id=ID" +
                                        " END " +
                                        " ELSE " +
                                        " BEGIN " +
                                        " insert into niktest2(Id,Name,Screenname,Profimg,InsertDateTime) values('" + ID + "','" + Text + "','" + Name + "','" + Screenname + "' ,'" + DateTime.Now.ToString() + "')" +
                                        " END ";
            command = new SqlCommand(sql, connection);
            adpter.InsertCommand = command;
            adpter.InsertCommand.ExecuteNonQuery();
        }
    }

运行此代码后,即使我的 xml 文件有更多数据,也只会更新第一行。 我想将所有数据插入到数据库中,并在 xml 文件中为其分配 id。 请帮忙..

【问题讨论】:

  • SQL 注入警告:对您的 XML 进行了几次编辑,您的数据库是我的(或消失了)……。 在您的 SQL 命令中使用参数。
  • 您使用的是什么 DBMS - SQL Server 2005、SQL Server 2008、Oracle、Postgresql 等?
  • 我是新来的,我不知道如何使用这个

标签: c# sql xml database sql-server-2008


【解决方案1】:

只要你插入了一行,这个条件就会成立:

If Exists(Select * from niktest2 Where Id  = ID)

因此您将执行更新而不是插入,因此您只会在数据库中获得一行。

由于您使用的是 SQL Server 2008,我将采用完全不同的方法,使用 Parameterised queriesMERGEtable valued parameters

第一步是创建你的表值参数(我不得不猜测你的类型:

CREATE TYPE dbo.nicktestTableType AS TABLE
(   
    Id INT,
    Name VARCHAR(255),
    Screenname VARCHAR(255),
    Profimg VARCHAR(255)
);

然后您可以编写MERGE 语句以更新插入数据库:

MERGE nicktest WITH (HOLDLOCK) AS t
USING @NickTestType AS s
    ON s.ID = t.ID
WHEN MATCHED THEN 
    UPDATE
    SET Name = s.Name,
        Screenname = s.Screenname,
        Profimg = s.Profimg,
        InsertDateTime = GETDATE()
WHEN NOT MATCHED THEN 
    INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
    VALUES (s.Id, s.Name, s.Screenname, s.Profimg, GETDATE());

然后你可以将你的数据表作为参数传递给查询:

using (var command = new SqlCommand(sql, connection))
{
    var parameter = new SqlParameter("@NickTestType", SqlDbType.Structured);
    parameter.Value = ds.Tables[0];
    parameter.TypeName = "dbo.nicktestTableType";
    command.Parameters.Add(parameter);
    command.ExecuteNonQuery();
}

如果您不想进行如此剧烈的更改,那么您至少应该使用参数化查询,因此您的 SQL 将是:

IF EXISTS (SELECT 1 FROM nicktest WHERE ID = @ID)
BEGIN
    UPDATE  nicktest
    SET     Name = @Name,
            ScreenName = @ScreeName,
            InsertDateTime = GETDATE()
    WHERE   ID = @ID;
END
ELSE
BEGIN
    INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
    VALUES (@ID, @Name, @Screenname, @Profimg, GETDATE());
END

或者最好仍然使用MERGE 作为HOLDLOCK 表提示防止(或至少大大减少)race condition

MERGE nicktest WITH (HOLDLOCK) AS t
USING (VALUES (@ID, @Name, @ScreenName, @ProfImg)) AS s (ID, Name, ScreenName, ProfImg)
    ON s.ID = t.ID
WHEN MATCHED THEN 
    UPDATE
    SET Name = s.Name,
        Screenname = s.Screenname,
        Profimg = s.Profimg,
        InsertDateTime = GETDATE()
WHEN NOT MATCHED THEN 
    INSERT (Id, Name, Screenname, Profimg, InsertDateTime)
    VALUES (s.Id, s.Name, s.Screenname, s.Profimg, GETDATE());

虽然使用表值参数,但这将比第一个解决方案效率低得多 那么你的 c# 会是这样的:

for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
    using (var command = new SqlCommand(sql, connection))
    {
        command.Parameters.AddWithValue("@ID", ds.Tables[0].Rows[i][0]);
        command.Parameters.AddWithValue("@Name", ds.Tables[0].Rows[i][1]);
        command.Parameters.AddWithValue("@ScreeName", ds.Tables[0].Rows[i][2]);
        command.Parameters.AddWithValue("@ProfImg", ds.Tables[0].Rows[i][3]);
        command.ExecuteNonQuery();
    }
}

【讨论】:

  • 这可以解决问题,但我建议您稍微改变一下思路,不要运行这种 RBAR 样式(Row By Agonizing Row)。我建议使用其他东西,也许使用 SqlBulkCopy 暂存数据,然后使用 MERGE 语句。或者构建一个 CTE 并将其合并到一个大的 SQL 字符串中。 (注射警告)。但是 RBAR 并不是一个好主意..
  • @atom.gregg 看来我在您的评论前 7 秒进行的编辑将涵盖您的建议 :)
  • 不错的更新,抱歉我在发帖之前错过了它。由于您的回答的完整性,投票支持您。
  • @GarethD 还有一件事...如何下载图片,链接是 XML 文件直接到本地文件夹...
  • 您必须反复执行此操作,即遍历您的数据表并单独下载每个数据表。您是将图像存储为二进制文件,还是仅存储文件路径?
猜你喜欢
  • 2014-03-13
  • 1970-01-01
  • 1970-01-01
  • 2019-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多