我的表格将包含许多重复字符串,例如域名。为了最小化数据库大小,我想在其他表中仅保存唯一域,并在主表中使用域ID。
我一直手动完成,但不久前我发现SQLite可以自动完成。
现在我尝试与" FOREIGN"使用多对一关系。关键,但没有成功。 也许我做错了什么。
示例代码
表类:
public class Domains
{
public Domains() { }
public Domains(string domain) { this.Domain = domain; }
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[Unique, MaxLength(64)]
public string Domain { get; set; }
}
public class Statistics
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Timestamp { get; set; }
[ForeignKey(typeof(Domains))]
public int DomainId { get; set; }
public int Status { get; set; }
[ManyToOne(CascadeOperations = CascadeOperation.All)]
public Domains Domain { get; set; }
}
主要代码:
static void Main(string[] args)
{
var dbFile = "stats.db";
var domains = new[] { "stackoverflow.com", "superuser.com", "serverfault.com", "google.com", "microsoft.com" };
var statList = new List<Statistics>();
var sqlBase = new SQLiteConnection(dbFile);
sqlBase.Execute("PRAGMA foreign_keys = ON");
sqlBase.CreateTable<Domains>();
sqlBase.CreateTable<Statistics>();
Console.WriteLine(SQLite3.LibVersionNumber());
var runTimestamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
foreach (var domain in domains)
{
HttpWebResponse resp = null;
var status = -1;
try
{
resp = (HttpWebResponse)WebRequest.Create("http://" + domain).GetResponse();
}
catch { };
status = (int)resp.StatusCode;
var stat = new Statistics();
stat.Domain = new Domains(domain);
stat.Status = status;
stat.Timestamp = runTimestamp;
statList.Add(stat);
}
sqlBase.InsertOrIgnoreAllWithChildren(statList); // Modification "INSERT" with "OR IGNORE"
Console.WriteLine(@"Table ""Domains""");
foreach (var table in sqlBase.Table<Domains>())
{
Console.WriteLine("Id: {0}\tDomain: {1}", table.Id, table.Domain);
}
Console.WriteLine();
Console.WriteLine(@"Table ""Statistics""");
foreach (var table in sqlBase.Table<Statistics>())
{
Console.WriteLine("Id: {0}\tDomain Id: {1}", table.Id, table.DomainId);
}
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
首次运行后,它看起来很好。
但是在第二次运行之后,当域重复时 - sqlite扩展插入错误的域id
我犯错误的地方?
答案 0 :(得分:1)
在您的代码中,每次尝试保存新统计信息时,您都会创建新的Domains
实体。
SQLite-Net Extensions需要引用对象的主键才能分配外键。您的InsertOrIgnoreAllWithChildren
似乎正在为所有10
个对象分配Domains
,即使这些对象未被插入。
您需要做的是获取当前域以获取正确的主键。
尝试这样的事情:
var dbFile = "stats.db";
var statList = new List<Statistics>();
var sqlBase = new SQLiteConnection(dbFile);
sqlBase.Execute("PRAGMA foreign_keys = ON");
sqlBase.CreateTable<Domains>();
sqlBase.CreateTable<Statistics>();
// Fetch existing domains from database
var domains = sqlBase.Table<Domains>().toList();
if (domains.isEmpty()) {
// Insert domains into database if they don't exist
var domainNames = new[] { "stackoverflow.com", "superuser.com", "serverfault.com", "google.com", "microsoft.com" };
domains = domainNames.Select(domainName => new Domain(domainName));
sqlBase.InsertAll(domains);
}
var runTimestamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
foreach (var domain in domains)
{
HttpWebResponse resp = null;
var status = -1;
try
{
resp = (HttpWebResponse)WebRequest.Create("http://" + domain.domain).GetResponse();
}
catch { };
status = (int)resp.StatusCode;
var stat = new Statistics();
stat.Domain = domain; // Assign the existing domain object
stat.Status = status;
stat.Timestamp = runTimestamp;
statList.Add(stat);
}
// Insert only Statistics (Domains already exist), and assign foreign keys
sqlBase.InsertAllWithChildren(statList);
答案 1 :(得分:1)
您可以使用while read -r line;do
exclude+=$(find . -type f -path "./$line")$'\n'
done <.rmignore
echo "ignored files:"
printf '%s\n' "$exclude"
echo "files to be deleted"
echo rm $(LC_ALL=C sort <(find . -type f) <(printf '%s\n' "$exclude") |uniq -u ) #intentionally non quoted to remove new lines
作为主键(和外键),并且您当前的代码将按预期工作:
Domain
答案 2 :(得分:0)
我的错误,功能&#34;外键&#34;不像我想的那样奏效。 所以我使用view连接两个表和触发器进行编辑视图。
它可以随时使用。
SQLite.Net和SQLite.Net.Extensions不支持视图和触发器,所以我只使用SQLite.Net和Execute-function来创建视图和触发器。
CREATE VIEW IF NOT EXISTS 'StatisticsView' AS
SELECT Stat.Id, Stat.Timestamp, Dom.Domain, Stat.Status FROM Statistics AS Stat
INNER JOIN Domains Dom ON Stat.DomainId = Dom.Id
CREATE TRIGGER IF NOT EXISTS 'StatisticsViewInsert'
INSTEAD OF INSERT ON 'StatisticsView'
BEGIN
INSERT OR IGNORE INTO Domains(Domain) VALUES(NEW.Domain);
INSERT INTO Statistics(Timestamp, Status, DomainId) VALUES (NEW.Timestamp, NEW.Status, (SELECT Id FROM Domains WHERE Domain = NEW.Domain));
END
CREATE TRIGGER IF NOT EXISTS 'StatisticsViewUpdate'
INSTEAD OF UPDATE ON 'StatisticsView'
BEGIN
INSERT OR IGNORE INTO Domains(Domain) VALUES(NEW.Domain);
UPDATE Statistics SET Status = NEW.Status, Timestamp = NEW.Timestamp, DomainId = (SELECT Id FROM Domains WHERE Domain = NEW.Domain) WHERE Id = OLD.Id;
END
CREATE TRIGGER IF NOT EXISTS 'StatisticsViewDelete'
INSTEAD OF DELETE ON 'StatisticsView'
BEGIN
DELETE FROM Domains WHERE (Domain = OLD.Domain AND (SELECT COUNT(Id) FROM Statistics WHERE DomainId = (SELECT Id FROM Domains WHERE Domain = OLD.Domain)) < 2);
DELETE FROM Statistics WHERE Id=OLD.Id;
END
表类:
public class Domains
{
public Domains() { }
public Domains(string domain) { this.Domain = domain; }
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[Unique, MaxLength(64)]
public string Domain { get; set; }
}
public class Statistics
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Timestamp { get; set; }
public int DomainId { get; set; }
public int Status { get; set; }
}
// Virtual Table
public class StatisticsView
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Timestamp { get; set; }
public string Domain { get; set; }
public int Status { get; set; }
}
主要代码:
static void Main(string[] args)
{
var dbFile = "stats.db";
var domains = new[] { "stackoverflow.com", "superuser.com", "serverfault.com", "google.com", "microsoft.com" };
var statList = new List<StatisticsView>();
var sqlBase = new SQLiteConnection(dbFile);
sqlBase.CreateTable<Domains>();
sqlBase.CreateTable<Statistics>();
sqlBase.Execute("CREATE VIEW IF NOT EXISTS 'StatisticsView' AS SELECT Stat.Id, Stat.Timestamp, Dom.Domain, Stat.Status FROM Statistics AS Stat INNER JOIN Domains Dom ON Stat.DomainId=Dom.Id;");
sqlBase.Execute("CREATE TRIGGER IF NOT EXISTS 'StatisticsViewInsert' INSTEAD OF INSERT ON 'StatisticsView' BEGIN INSERT OR IGNORE INTO Domains(Domain) VALUES(NEW.Domain); INSERT INTO Statistics(Timestamp, Status, DomainId) VALUES (NEW.Timestamp, NEW.Status, (SELECT Id FROM Domains WHERE Domain=NEW.Domain)); END");
sqlBase.Execute("CREATE TRIGGER IF NOT EXISTS 'StatisticsViewUpdate' INSTEAD OF UPDATE ON 'StatisticsView' BEGIN INSERT OR IGNORE INTO Domains(Domain) VALUES(NEW.Domain); UPDATE Statistics SET Status=NEW.Status, Timestamp=NEW.Timestamp, DomainId=(SELECT Id FROM Domains WHERE Domain=NEW.Domain) WHERE Id=OLD.Id; END");
sqlBase.Execute("CREATE TRIGGER IF NOT EXISTS 'StatisticsViewDelete' INSTEAD OF DELETE ON 'StatisticsView' BEGIN DELETE FROM Domains WHERE (Domain = OLD.Domain AND (SELECT COUNT(Id) FROM Statistics WHERE DomainId=(SELECT Id FROM Domains WHERE Domain=OLD.Domain)) < 2); DELETE FROM Statistics WHERE Id=OLD.Id; END");
Console.WriteLine(SQLite3.LibVersionNumber());
var runTimestamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
foreach (var domain in domains)
{
HttpWebResponse resp = null;
var status = -1;
try
{
resp = (HttpWebResponse)WebRequest.Create("http://" + domain).GetResponse();
}
catch { };
status = (int)resp.StatusCode;
var stat = new StatisticsView();
stat.Domain = domain;
stat.Status = status;
stat.Timestamp = runTimestamp;
statList.Add(stat);
}
sqlBase.InsertAll(statList);
Console.WriteLine(@"Table ""Domains""");
foreach (var table in sqlBase.Table<Domains>())
{
Console.WriteLine("Id: {0}\tDomain: {1}", table.Id, table.Domain);
}
Console.WriteLine();
Console.WriteLine(@"Table ""Statistics""");
foreach (var table in sqlBase.Table<Statistics>())
{
Console.WriteLine("Id: {0}\tDomain Id: {1}", table.Id, table.DomainId);
}
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
第二次运行后: