【问题标题】:How can I conditionally prevent a column from being updated in SQLite?如何有条件地防止在 SQLite 中更新列?
【发布时间】:2015-02-10 21:55:34
【问题描述】:

我有一个包含 CREATED 和 MODIFIED 列的表。我只想插入一次 CREATED 值,然后让它不可变。我知道如何以乏味的方式执行此操作(编写“DoesRecordExist()”方法,然后根据该方法更改查询和查询参数的数量),但肯定有一种更巧妙的方法可以完成此操作。毕竟,这必须是一个共同的要求(如果你愿意的话,一个“数据库模式”)。

我的代码是这样的:

public void InsertUserSiteRecord(UserSite us)
{
    using (SQLiteConnection conn = new SQLiteConnection(HHSUtils.GetDBConnection()))
    {
        conn.Open();
        using (SQLiteCommand cmd = new SQLiteCommand(conn))
        {
            cmd.CommandText =
                String.Format(
                    @"INSERT INTO UserSite (SiteNum, SerialNum, UserName, Created, Modified) 
                                VALUES (@SiteNum, @SerialNum, @UserName, @Created, @Modified)");
            cmd.Parameters.Add(new SQLiteParameter("SiteNum", us.SiteNum));
            cmd.Parameters.Add(new SQLiteParameter("SerialNum", us.SerialNum));
            cmd.Parameters.Add(new SQLiteParameter("UserName", us.UserName));
            cmd.Parameters.Add(new SQLiteParameter("Created", us.Created));
            cmd.Parameters.Add(new SQLiteParameter("Modified", us.Modified));

            cmd.ExecuteNonQuery();
        }
        conn.Close();
    }
}

...我想避免这样做:

public void InsertUserSiteRecord(UserSite us)
{
    using (SQLiteConnection conn = new SQLiteConnection(HHSUtils.GetDBConnection()))
    {
        conn.Open();
        using (SQLiteCommand cmd = new SQLiteCommand(conn))
        {
            if (!RecordExists(us.SiteNum, us.SerialNum, us.UserName))
            {
                cmd.CommandText =
                    String.Format(
                        @"INSERT INTO UserSite (SiteNum, SerialNum, UserName, Created, Modified) 
                                    VALUES (@SiteNum, @SerialNum, @UserName, @Created, @Modified)");
            else
            {
                cmd.CommandText =
                    String.Format(
                        @"INSERT INTO UserSite (SiteNum, SerialNum, UserName, Modified) 
                                    VALUES (@SiteNum, @SerialNum, @UserName, @Modified)");
            }
            cmd.Parameters.Add(new SQLiteParameter("SiteNum", us.SiteNum));
            cmd.Parameters.Add(new SQLiteParameter("SerialNum", us.SerialNum));
            cmd.Parameters.Add(new SQLiteParameter("UserName", us.UserName));
            if (!RecordExists(us.SiteNum, us.SerialNum, us.UserName))
            {
                cmd.Parameters.Add(new SQLiteParameter("Created", us.Created));
            }
            cmd.Parameters.Add(new SQLiteParameter("Modified", us.Modified));

            cmd.ExecuteNonQuery();
        }
        conn.Close();
    }
}

private bool RecordExists(String SiteNum, String SerialNum, String UserId)
{
    // query the table to see if those three values exist in any record
}

是否有类似的 SQL[ite] 构造:

cmd.Parameters.AddOnlyIfColumnIsEmpty(new SQLiteParameter("Created", us.Created));

?或者如何最好地解决这个问题?

更新

dub stylee 的回答很巧妙,但作为我自己,我走上了乏味但更容易理解的 camino 并创建了这三种方法:

public int UserSiteIdFor(String userName, String serialNum, String siteNum)
{
    int Id;
    const string qry = "SELECT Id FROM UserSite WHERE UserName =
@UserName AND SiteNum = @SiteNum AND SerialNum = @SerialNum";

    try
    {
        using (SQLiteConnection con = new
SQLiteConnection(HHSUtils.GetDBConnection()))
        {
            con.Open();
            SQLiteCommand cmd = new SQLiteCommand(qry, con);
            cmd.Parameters.Add(new SQLiteParameter("UserName",
userName));
            cmd.Parameters.Add(new SQLiteParameter("SiteNum", siteNum));
            cmd.Parameters.Add(new SQLiteParameter("SerialNum",
serialNum));
            Id = Convert.ToInt32(cmd.ExecuteScalar());
        }
    }
    catch (Exception ex)
    {
        String msgInnerExAndStackTrace = String.Format(
            "{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message,
ex.InnerException, ex.StackTrace);
        ExceptionLoggingService.Instance.WriteLog(String.Format("From
TestHHSDBUtils.UserSiteIdFor: {0}", msgInnerExAndStackTrace));
        return 0;
    }
    return Id;
}

public void InsertUserSiteRecord(UserSite us)
{
    using (SQLiteConnection conn = new
SQLiteConnection(HHSUtils.GetDBConnection()))
    {
        conn.Open();
        using (SQLiteCommand cmd = new SQLiteCommand(conn))
        {
            cmd.CommandText =
                String.Format(
                    @"INSERT INTO UserSite (SiteNum, SerialNum,
UserName, Created, Modified) 
                                VALUES (@SiteNum, @SerialNum, @UserName,
@Created, @Modified)");
            cmd.Parameters.Add(new SQLiteParameter("SiteNum",
us.SiteNum));
            cmd.Parameters.Add(new SQLiteParameter("SerialNum",
us.SerialNum));
            cmd.Parameters.Add(new SQLiteParameter("UserName",
us.UserName));
            cmd.Parameters.Add(new SQLiteParameter("Created",
us.Created));
            cmd.Parameters.Add(new SQLiteParameter("Modified",
us.Modified));

            cmd.ExecuteNonQuery();
        }
        conn.Close();
    }
}

public void UpdateUserSiteRecord(int Id, String lastLogin)
{
    using (SQLiteConnection conn = new
SQLiteConnection(HHSUtils.GetDBConnection()))
    {
        conn.Open();
        using (SQLiteCommand cmd = new SQLiteCommand(conn))
        {
            cmd.CommandText = String.Format(@"UPDATE UserSite SET
Modified = @Modified WHERE Id = @Id");
            cmd.Parameters.Add(new SQLiteParameter("Id", Id));
            cmd.Parameters.Add(new SQLiteParameter("Modified",
lastLogin));

            cmd.ExecuteNonQuery();
        }
        conn.Close();
    }
}

...然后这样称呼他们:

int userSiteId = hhsdbutils.UserSiteIdFor(us.UserName, us.SerialNum, us.SiteNum);
if (userSiteId > 0)
{
    hhsdbutils.UpdateUserSiteRecord(userSiteId, us.Modified);
}
else
{
    hhsdbutils.InsertUserSiteRecord(us);
}

有效。

【问题讨论】:

  • 您是否期望 CREATED 列为空?

标签: c# sql sqlite datecreated datemodified


【解决方案1】:

您需要检查记录是否存在;如果存在则执行 UPDATE(不更改您的 Created 值),如果不存在则执行 INSERT。

假设您的桌子上有一个 PK,您的原始代码将给出重复键错误。

【讨论】:

  • 是的,我知道;我想知道这种常见情况是否有更简单的方法。哦,你说得对,我在上面的任何一种情况下都在做 INSERT。傻我!
【解决方案2】:

您可以使用trigger 来完成此操作。在此处查看有关触发器的 SQLite 文档:https://www.sqlite.org/lang_createtrigger.html

基本上,您将创建一个INSTEAD OF 触发器,然后相应地设置您的查询。来自文档:

For an example of an INSTEAD OF trigger, consider the following schema:

CREATE TABLE customer(
  cust_id INTEGER PRIMARY KEY,
  cust_name TEXT,
  cust_addr TEXT
);
CREATE VIEW customer_address AS
   SELECT cust_id, cust_addr FROM customer;
CREATE TRIGGER cust_addr_chng
INSTEAD OF UPDATE OF cust_addr ON customer_address
BEGIN
  UPDATE customer SET cust_addr=NEW.cust_addr
   WHERE cust_id=NEW.cust_id;
END;
With the schema above, a statement of the form:

UPDATE customer_address SET cust_addr=$new_address WHERE cust_id=$cust_id;
Causes the customer.cust_addr field to be updated for a specific customer entry that has customer.cust_id equal to the $cust_id parameter. Note how the values assigned to the view are made available as field in the special "NEW" table within the trigger body.

您想要做的只是将触发器设置为不更新该列,即使原始查询通过该列进行更新。

【讨论】:

  • 很好的答案,但我尽量保持我的代码“Grammatik”分数低(保持阅读等级尽可能低,以备将来维护)。我保存的 Excedrin 可能是我自己的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
  • 2018-12-29
  • 2019-05-13
  • 1970-01-01
  • 2016-08-15
  • 2018-06-27
  • 2020-05-23
相关资源
最近更新 更多