草庐IT

c# - 独立于 DBMS 的查询

coder 2023-10-21 原文

我的硕士论文是关于通过分析元数据和存储的数据来发现糟糕的数据库设计。为此,我们从给定的 DBMS 中提取元数据模型,然后在该元数据上运行一组规则。

要通过数据分析扩展这个过程,我们需要允许规则直接查询数据库,但我们必须保持 DBMS 独立性,以便查询可以应用于 PostgreSQL、MSSQL 和 MySQL。

我们讨论了一种查询的函数式构造,例如:

new Query(new Select(columnID), new From(tableID), new Where(new Equality(columnID1, columnID2)))

然后使用特定于 DBMS 的序列化程序。

另一种方法是让规则自己处理:

public Query QueryDatabase(DBMS dbms)
{
 if (dbms == PostgreSQL) { return "select count(1) from Users"}
 if (dbms == MSSQL) {return ....}
}

我们是否遗漏了什么?所有这些实际上都存在于某个不错的图书馆中吗?是的,我们已经研究了 Entity Framework ,但它们似乎依赖于数据库的静态类型模型,由于显而易见的原因无法创建。

我应该提一下,我们维护着一个可扩展的规则架构,允许最终用户实现他们自己的规则。

为了弄清楚我们要实现什么,看下面的查询(mssql),它需要两个参数,表名(@table)和列名(@column):

DECLARE @TotalCount FLOAT;
SELECT @TotalCount = COUNT(1) FROM [@table];
SELECT SUM(pcount * LOG10(@TotalCount / pcount)) / (LOG10(2) * @TotalCount)  
FROM (SELECT (Count([@column])) as pcount 
      FROM [@table]
      GROUP BY [@column])  as exp1 

查询通过估计熵来测量存储在给定属性中的信息量。它需要访问表中的所有行。为避免从数据库中提取所有行并通过慢速网络连接传输它们,最好用 SQL 表示它们,只传输一个数字。

注意:我们确实拥有我们需要的所有元数据。本题仅用于访问数据!

我不太确定是将这个添加到我已经很长的问题中,编辑现有答案还是要做什么。请随时指教。 ;)

基于我的回答:

new Query()
.Variable(varname => FLOAT)
.Set(varname => new Query().Count(1).From(table) )
.Select(new Aggregate().Sum(varname => "pcount * LOG10(varname / pcount)"))
.From(
  new Query()
  .Select(pcount => new Aggregate().Count(column)
  .From(table)
  .GroupBy(column)
)

抛开语法错误和 lambda 语句的误用不谈,我尝试使用一些扩展方法来构建查询。这看起来确实是一种相当复杂的方法。您如何看待这种方法?

基于 LINQ 答案:

let totalCount = Table.Count
from uv un from r in Table
           group r by r["attr"]
           select r.Count
select r.Count * Log2((totalCount / r.Count))

看起来相当不错,但是要实现的东西太多了......

最佳答案

您可以通过实现自定义 LINQ provider 来实现相同的目的基础设施。查询是通用的,但是 AST tree visitors生成 SQL 查询的函数可以插入。您甚至可以使用内存数据存储来模拟数据库,并将您的自定义 LINQ 查询转换为 LINQ to objects 查询!

您需要创建一个知道如何从对象的索引器中提取列名的提供程序。这是您可以扩展的基本框架:

// Runs in LinqPad!
public class TableQueryObject
{
    private readonly Dictionary<string, object> _data = new Dictionary<string, object>();
    public string TableName { get; set; }
    public object this[string column]
    {
        get { return _data.ContainsKey(column) ? _data[column] : null; }
        set { if (_data.ContainsKey(column)) _data[column] = value; else _data.Add(column, value); }
    }
}

public interface ITableQuery : IEnumerable<TableQueryObject>
{
    string TableName { get; }
    string ConnectionString { get; }
    Expression Expression { get; }
    ITableQueryProvider Provider { get; }
}

public interface ITableQueryProvider
{
    ITableQuery Query { get; }
    IEnumerable<TableQueryObject> Execute(Expression expression);
}

public interface ITableQueryFactory
{
    ITableQuery Query(string tableName);
}


public static class ExtensionMethods
{
    class TableQueryContext : ITableQuery
    {
        private readonly ITableQueryProvider _queryProvider;
        private readonly Expression _expression;

        public TableQueryContext(ITableQueryProvider queryProvider, Expression expression)
        {
            _queryProvider = queryProvider;
            _expression = expression;
        }

        public string TableName { get { return _queryProvider.Query.TableName; } }
        public string ConnectionString { get { return _queryProvider.Query.ConnectionString; } }
        public Expression Expression { get { return _expression; } }
        public ITableQueryProvider Provider { get { return _queryProvider; } }
        public IEnumerator<TableQueryObject> GetEnumerator() { return Provider.Execute(Expression).GetEnumerator(); }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }

    public static MethodInfo MakeGeneric(MethodBase method, params Type[] parameters)
    {
        return ((MethodInfo)method).MakeGenericMethod(parameters);
    }

    public static Expression StaticCall(MethodInfo method, params Expression[] expressions)
    {
        return Expression.Call(null, method, expressions);
    }

    public static ITableQuery CreateQuery(this ITableQueryProvider source, Expression expression)
    {
        return new TableQueryContext(source, expression);
    }

    public static IEnumerable<TableQueryObject> Select<TSource>(this ITableQuery source, Expression<Func<TSource, TableQueryObject>> selector)
    {
        return source.Provider.CreateQuery(StaticCall(MakeGeneric(MethodBase.GetCurrentMethod(), typeof(TSource)), source.Expression, Expression.Quote(selector)));
    }

    public static ITableQuery Where(this ITableQuery source, Expression<Func<TableQueryObject, bool>> predicate)
    {
        return source.Provider.CreateQuery(StaticCall((MethodInfo)MethodBase.GetCurrentMethod(), source.Expression, Expression.Quote(predicate)));
    }
}

class SqlTableQueryFactory : ITableQueryFactory
{

    class SqlTableQuery : ITableQuery
    {
        private readonly string _tableName;
        private readonly string _connectionString;
        private readonly ITableQueryProvider _provider;
        private readonly Expression _expression;

        public SqlTableQuery(string tableName, string connectionString)
        {
            _connectionString = connectionString;
            _tableName = tableName;
            _provider = new SqlTableQueryProvider(this);
            _expression = Expression.Constant(this);
        }

        public IEnumerator<TableQueryObject> GetEnumerator() { return Provider.Execute(Expression).GetEnumerator(); }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
        public string TableName { get { return _tableName; } }
        public string ConnectionString { get { return _connectionString; } }
        public Expression Expression { get { return _expression; } }
        public ITableQueryProvider Provider { get { return _provider; } }
    }

    class SqlTableQueryProvider : ITableQueryProvider
    {
        private readonly ITableQuery _query;
        public ITableQuery Query { get { return _query; } }
        public SqlTableQueryProvider(ITableQuery query) { _query = query; }

        public IEnumerable<TableQueryObject> Execute(Expression expression)
        {
            //var connecitonString = _query.ConnectionString;
            //var tableName = _query.TableName;
            // TODO visit expression AST (generate any sql dialect you want) and execute resulting sql
                    // NOTE of course the query can be easily parameterized!
            // NOTE here the fun begins, just return some dummy data for now :)
            for (int i = 0; i < 100; i++)
            {
                var obj = new TableQueryObject();
                obj["a"] = i;
                obj["b"] = "blah " + i;
                yield return obj;
            }
        }
    }

    private readonly string _connectionString;
    public SqlTableQueryFactory(string connectionString) { _connectionString = connectionString; }
    public ITableQuery Query(string tableName)
    {
        return new SqlTableQuery(tableName, _connectionString);
    }
}

static void Main()
{
    ITableQueryFactory database = new SqlTableQueryFactory("SomeConnectionString");
    var result = from row in database.Query("myTbl")
                 where row["someColumn"] == "1" && row["otherColumn"] == "2"
                 where row["thirdColumn"] == "2" && row["otherColumn"] == "4"
                 select row["a"]; // NOTE select executes as linq to objects! FTW
    foreach(var a in result) 
    {
        Console.WriteLine(a);
    }   
}

关于c# - 独立于 DBMS 的查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5063760/

有关c# - 独立于 DBMS 的查询的更多相关文章

  1. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  2. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  3. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  4. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  5. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  6. sql - 查询忽略时间戳日期的时间范围 - 2

    我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时

  7. ruby-on-rails - solr 清理查询 - 2

    我在Rails上使用带有ruby​​的solr。一切正常,我只需要知道是否有任何现有代码来清理用户输入,比如以?开头的查询。或* 最佳答案 我不知道执行此操作的任何代码,但理论上可以通过查看parsingcodeinLucene来完成并搜索thrownewParseException(只有16个匹配!)。在实践中,我认为您最好只捕获代码中的任何solr异常并显示“无效查询”消息或类似信息。编辑:这里有几个“sanitizer”:http://pivotallabs.com/users/zach/blog/articles/937-s

  8. ruby-on-rails - Rails 3 在一个查询中包含多个表 - 2

    我正在为锦标赛开发一个Rails应用程序。我在这个查询中使用了三个模型:classPlayertruehas_and_belongs_to_many:tournamentsclassTournament:destroyclassPlayerMatch"Player",:foreign_key=>"player_one"belongs_to:player_two,:class_name=>"Player",:foreign_key=>"player_two"在tournaments_controller的显示操作中,我调用以下查询:Tournament.where(:id=>params

  9. ruby-on-rails - Sunspot:如何对具有不同值的多个字段进行全文查询? - 2

    我想用sunspot重现以下原始solr查询q=exact_term_text:fooORterm_textv:foo*ORalternate_text:bar*但我无法通过标准的太阳黑子界面理解这是否可能以及如何实现,因为看起来:fulltext方法似乎不接受多个文本/搜索字段参数我不知道将什么参数作为第一个参数传递给fulltext,就好像我通过了"foo"或"bar"结果不匹配如果我传递一个空参数,我得到一个q=*:*范围过滤器(例如with(:term).starting_with('foo*')(顾名思义)作为过滤器查询应用,因此不参与评分。似乎可以手动编写字符串(或者可能使

  10. ruby-on-rails - 在不重新查询数据库的情况下重新排序 Rails 中的事件记录? - 2

    例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果

随机推荐