在SQLite中存储图形 - 关系还是平面?

时间:2015-02-02 21:26:03

标签: c# sqlite graph directed-graph

问题

我需要在sqlite数据库中存储有向图,我想知道如何解决这个问题。

我的第一个解决方案是创建两个表,一个用于节点,一个用于边缘,但是与问题的非关系模型相比,我对插入性能不满意。

给定一个100k节点的典型图表,每个节点有50个传出边缘,插入关系模型需要大约20s vs 7s使用平面方法。

实际问题

  • 我有什么办法可以加快使用关系模型将图表插入数据库的过程吗?
  • 我是否以次优的方式使用SQLite API?
  • 我是否应该考虑一种关系方法,因为它不是必需的,只是"很好"?

其他信息

我已经写了两个样本来证明我的问题:

关系方法

    public sealed class RelationalGraphDatabase
        : IDatabase
    {
        private readonly SQLiteConnection _connection;

        public RelationalGraphDatabase()
        {
            const string fname = @"E:\relational_graph.db";
            if (File.Exists(fname))
                File.Delete(fname);

            var conn = new SQLiteConnectionStringBuilder
                {
                    DataSource = fname
                };
            _connection = new SQLiteConnection(conn.ToString());
            _connection.Open();
            using (
                var command = new SQLiteCommand("CREATE TABLE Node (Id INTEGER PRIMARY KEY, Name TEXT)", _connection))
            {
                command.ExecuteNonQuery();
            }

            using (
                var command =
                    new SQLiteCommand(
                        "CREATE TABLE Edges (Node_From INTEGER NOT NULL, Node_To INTEGER, P1 INTEGER NOT NULL, P2 INTEGER NOT NULL, P3 INTEGER NOT NULL, P4 INTEGER NOT NULL)",
                        _connection))
            {
                command.ExecuteNonQuery();
            }
        }

        public void Commit(IEnumerable<Node> values)
        {
            using (SQLiteTransaction transaction = _connection.BeginTransaction())
            {
                using (var command = new SQLiteCommand("INSERT INTO Node (Id, Name) VALUES (@Id, @Name)", _connection))
                {
                    command.Parameters.Add("@Id", DbType.Int64);
                    command.Parameters.Add("@Name", DbType.String);

                    foreach (Node bts in values)
                    {
                        command.Parameters[0].Value = bts.Id;
                        command.Parameters[1].Value = bts.Name;
                        command.ExecuteNonQuery();
                    }
                }

                using (
                    var command =
                        new SQLiteCommand("INSERT INTO Edges (Node_From, Node_To, P1, P2, P3, P4) VALUES (@From, @To, @P1, @P2, @P3, @P4)",
                                          _connection))
                {
                    command.Parameters.Add("@From", DbType.Int64);
                    command.Parameters.Add("@To", DbType.Int64);
                    command.Parameters.Add("@P1", DbType.UInt16);
                    command.Parameters.Add("@P2", DbType.UInt16);
                    command.Parameters.Add("@P3", DbType.UInt16);
                    command.Parameters.Add("@P4", DbType.UInt16);

                    foreach (Node bts in values)
                    {
                        foreach (NodeId neighbour in bts.Edges)
                        {
                            command.Parameters[0].Value = (long) bts.Id;
                            command.Parameters[1].Value = null;
                            command.Parameters[2].Value = neighbour.P1;
                            command.Parameters[3].Value = neighbour.P2;
                            command.Parameters[4].Value = neighbour.P3;
                            command.Parameters[5].Value = neighbour.P4;
                            command.ExecuteNonQuery();
                        }
                    }
                }

                transaction.Commit();
            }
        }

        public IEnumerable<Node> Select()
        {
            var ret = new Dictionary<uint, Node>();
            using (var command = new SQLiteCommand("SELECT * FROM Node", _connection))
            {
                using (SQLiteDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var bts = new Node
                            {
                                Id = (uint) reader.GetInt64(0),
                                Name = reader.GetString(1),
                                Edges = new List<NodeId>()
                            };
                        ret.Add(bts.Id, bts);
                    }
                }
            }

            string sql = string.Format("SELECT * FROM Edges WHERE Node_From IN ({0})", string.Join(", ", ret.Keys));
            using (var command = new SQLiteCommand(sql, _connection))
            {
                using (SQLiteDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var id = (uint) reader.GetInt64(0);
                        int mcc = reader.GetInt32(2);
                        int mnc = reader.GetInt32(3);
                        int lac = reader.GetInt32(4);
                        int ci = reader.GetInt32(5);
                        ret[id].Edges.Add(new NodeId
                            {
                                P1 = (ushort) mcc,
                                P2 = (ushort) mnc,
                                P3 = (ushort) lac,
                                P4 = (ushort) ci
                            });
                    }
                }
            }

            return ret.Values.ToList();
        }

        public void Dispose()
        {
            _connection.Dispose();
        }
    }

扁平方法

public sealed class FlatGraphDatabase
    : IDatabase
{
    private readonly SQLiteConnection _connection;

    public FlatGraphDatabase()
    {
        const string fname = @"E:\flat_graph.db";
        if (File.Exists(fname))
            File.Delete(fname);

        var conn = new SQLiteConnectionStringBuilder
            {
                DataSource = fname
            };
        _connection = new SQLiteConnection(conn.ToString());
        _connection.Open();
        using (
            var command = new SQLiteCommand("CREATE TABLE Node (Id INTEGER PRIMARY KEY, Name TEXT, Edges TEXT)", _connection))
        {
            command.ExecuteNonQuery();
        }
    }

    public void Commit(IEnumerable<Node> values)
    {
        using (var transaction = _connection.BeginTransaction())
        using (var command = new SQLiteCommand("INSERT INTO Node (Id, Name, Edges) VALUES (@Id, @Name, @Edges)", _connection))
        {
            command.Parameters.Add("@Id", DbType.Int64);
            command.Parameters.Add("@Name", DbType.String);
            command.Parameters.Add("@Edges", DbType.String);

            foreach (var bts in values)
            {
                command.Parameters[0].Value = bts.Id;
                command.Parameters[1].Value = bts.Name;
                command.Parameters[2].Value = string.Join(",", bts.Edges);
                command.ExecuteNonQuery();
            }

            transaction.Commit();
        }
    }

    public IEnumerable<Node> Select()
    {
        var ret = new List<Node>();
        using (var command = new SQLiteCommand("SELECT * FROM Node", _connection))
        {
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    var bts = new Node
                        {
                            Id = (uint) reader.GetInt64(0),
                            Name = reader.GetString(1),
                            Edges = reader.GetString(2).Split(',').Select(NodeId.Parse).ToList()
                        };
                    ret.Add(bts);
                }
            }
        }
        return ret;
    }

    public void Dispose()
    {
        _connection.Dispose();
    }
}

感谢您提前的时间:)

0 个答案:

没有答案