如何有条件地阻止列在 SQLite 中更新?
How can I conditionally prevent a column from being updated in SQLite?
我有一个包含 CREATED 和 MODIFIED 列的 table。我只想插入一次 CREATED 值,然后让它成为 immutable。我知道如何以繁琐的方式完成此操作(编写 "DoesRecordExist()" 方法,然后根据该方法更改查询和查询参数的数量),但肯定有更巧妙的方法来完成此操作。毕竟,这必须是一个常见的要求(如果您愿意,可以 "database pattern")。
我的代码是这样的:
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);
}
有效。
您可以使用 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.
您要做的只是将触发器设置为不更新该列,即使原始查询传入要更新的列也是如此。
您需要检查记录是否存在;如果存在则执行更新(不更改您的创建值),如果不存在则执行插入。
假设您在 table.
上有 PK,您的原始代码将给出重复键错误
我有一个包含 CREATED 和 MODIFIED 列的 table。我只想插入一次 CREATED 值,然后让它成为 immutable。我知道如何以繁琐的方式完成此操作(编写 "DoesRecordExist()" 方法,然后根据该方法更改查询和查询参数的数量),但肯定有更巧妙的方法来完成此操作。毕竟,这必须是一个常见的要求(如果您愿意,可以 "database pattern")。
我的代码是这样的:
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);
}
有效。
您可以使用 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.
您要做的只是将触发器设置为不更新该列,即使原始查询传入要更新的列也是如此。
您需要检查记录是否存在;如果存在则执行更新(不更改您的创建值),如果不存在则执行插入。
假设您在 table.
上有 PK,您的原始代码将给出重复键错误