代码之家  ›  专栏  ›  技术社区  ›  Mark Canlas

如何用面向对象的Perl组装SQL?

  •  9
  • Mark Canlas  · 技术社区  · 17 年前

    我目前负责一个似乎与数据库非常亲密的过程。我的程序/脚本/框架的目标是使不同的数据源保持一致。通过使用依赖注入的形式,我的过程在非常高的级别上运行良好。每种数据源类型的实现都隐藏在正在发生的最高级别的业务抽象中。太好了。我有两个问题。

    1) 我有一个很长的段落(正是这个长度让我很困扰),它在Perl空间中组装了一个SQL语句,说明如何将这些不同的数据源转换为一种同构的最终格式。因此,SQL字符串始终取决于我使用的数据类型。WHERE子句依赖,FROM子句依赖,INSERT子句依赖,一切都依赖。正是这种高度的依赖性让我感到困惑。我如何以面向对象的方式对这个过程进行建模?MagicObject->构建SQL?这基本上就是我现在所拥有的,但感觉代码的所有部分都知道得太多了,因此它的长度。

    2) 如果我有一个做某事的函数(构建SQL?),我是否会传入整个业务对象,然后在最后一刻将其字符串化?或者,我是否提前将它们串起来,只让我的函数处理它需要的东西,而不是渲染对象本身?

    编辑 :虽然我不怀疑ORM的重要性,但我认为我们还没有进入ORM领域。想象一下,美国、国家和虚构联盟的棒球数据都以不同的格式存储,具有不同程度的规范化。读取这些数据源并将其放入一个统一的、规范化的池中是我的过程的工作。我觉得作用于这些对象的ORM空间发生在我的过程之后。如果你愿意的话,我是一名数据管理员。由于缺乏我创建的统一池,基本上还没有业务对象可以操作。

    编辑^2 :我注意到,也许我没有足够详细地描述问题空间。这里有一个例子。

    想象一下,你必须建立一个美国所有罪犯的主数据库。贵公司的服务正在销售一种产品,该产品位于顶部,并以干净、统一的格式提供对这些数据的访问。

    这些数据由50个州公开提供,但格式截然不同。有些是一个数据文件,没有规范化。其他是CSV格式的标准化表格。有些是Excel文档。有些是TSV。有些记录甚至在没有人工干预的情况下是不完整的(其他手动创建的数据源)。

    我的项目的目的是为50个州中的每一个州制作一个“驱动程序”,并确保该过程的最终产品是一个完美的关系模型中的罪犯主数据库。所有键都正确,模式完美,等等。

    8 回复  |  直到 16 年前
        1
  •  9
  •   Aristotle Pagaltzis    17 年前

    你想看看 Fey 。我几个月前开始在工作中使用它,虽然由于年龄较小,实施仍然有一些困难,但它背后的想法是坚实的。例如,从手册中轻松改编一个查询:

    my $user = $schema->table( 'user' );
    my $q = Fey::SQL
        ->new_select
        ->select( $user->columns( 'user_id', 'username' ) )
        ->from( $user );
    

    现在你可以写一个这样的函数:

    sub restrict_with_group {
        my ( $q, $table, @group_id ) = @_;
        my $group = $schema->table( 'group' )->alias;
        $q
            ->from( $table, $group )
            ->where( $group->column( 'group_id' ), 'IN', @group_id );
    }
    

    这将从添加一个内部连接 user group 以及a WHERE 条件。瞧,你可以在主程序中编写以下内容:

    restrict_with_group( $q, $user, qw( 1 2 3 ) );
    

    但是这个 restrict_with_group 功能将工作 任何 具有外键的查询 桌子! 要使用它,您需要传递要限制的查询、要应用限制的表以及要限制它的组ID。

    最后你说 $q->sql( $dbh ) 您将返回一个SQL字符串,表示您在 $q 对象。

    因此,基本上Fey为您提供了原生SQL所缺少的抽象功能。您可以从查询中提取可重用的方面,并将其打包为单独的函数。

        2
  •  7
  •   jrockway    17 年前

    请不要编写自己的ORM。使用类似的东西 DBIx::Class .

    您提到的所有这些问题都已得到解决,并且该实现已在数千个其他应用程序中进行了测试。坚持编写你的应用程序,而不是重新实现库。你可能不会真的 使用 DBIC在你的应用程序中,但你应该看看它的实现方法;特别是它如何增量构建ResultSets(不是结果集,而是延迟查询)。

        3
  •  5
  •   Dave Rolsky    17 年前

    如果你 do not 的常用口语形式 想要一个ORM,但你想在没有直接字符串操作/连接的情况下从位组装SQL,看看 Fey ,这可能会做你想做的事。

    更新:Aristotle Pagaltzis的回答要好得多。他实际上给出了Fey的样子以及它如何帮助的例子。

        4
  •  1
  •   user3458 user3458    17 年前

    从纯粹的编码角度来看,你手头有一段又长又复杂的代码。你不喜欢,为什么?我只能假设其中存在一些代码重复。否则,有什么不喜欢的呢?因此,重构它以消除重复。..我知道这听起来很老套,但既然你不发布代码,就很难更具体了。可能有一个对象具有from、where和insert子句的方法,这样SQL的基础结构就不会重复?我确实不知道该怎么办,但消除重复是关键。

        5
  •  1
  •   Mike Woodhouse    17 年前

    除非我误解了,否则这似乎是一个ETL(提取/转换/加载)应用程序,它还没有找到将三个阶段分开的方法。

    如果输出模型只有一两个表,那么您可能也可以使用SQL。否则,特别是如果你插入的表之间存在关系,一个好的ORM应该能简化事情。

    采用50个状态的想法,你真的无法摆脱50个“提取”进程,希望有一个共享例程库。我会一次处理一个输入源的问题,在添加新输入源时进行重构,但要小心封装可变部分,这样当供应商更改其格式时,我就确切地知道需要在哪里进行更改。

    “转换”部分不应该太繁重:只需把你得到的东西准备好输出即可。

        6
  •  0
  •   Dave Swersky    17 年前

    我认为您描述的是动态SQL——在运行时以编程方式构建请求。这是LINQ to SQL和LLBLGenPro等对象关系映射器的一个常见功能。建造一个不是一项小任务。

    通常,ORM对象化SQL语言。您编写了一种“SQL文档对象模型(DOM)”,它允许您通过将SQL查询表示为(例如)“请求”对象来以编程方式构建SQL查询。然后,您可以在Request对象上设置属性,如Column集合、Table集合和Join集合(这些只是一种方法的示例)。结果将是一个SQL请求字符串,作为Request对象的属性公开。

    您还必须使Request对象能够读取数据源的模式定义。您提到WHERE子句依赖于类型。因此,您的SQL汇编器必须能够读取模式并适当地构建子句。

    这对你的案子来说可能太过分了。我认为最根本的问题是,你绝对需要动态SQL查询,还是有一个不太复杂的选项可以满足你的要求?

        7
  •  0
  •   Jack M.    17 年前

    在我看来,您解决这个问题的方法可能需要考虑一下。您目前有多个数据源,需要将其视为单个数据源。那么,为什么要将它们作为单独的数据源呢?

    根据数据更新的频率(或查看性能,访问频率),您可以将数据组合到SQLite等临时数据源中。如果每个州的数据都有一个转换器,可以将其从格式a转换为SQLite表中的通用格式,那么您可以使用您选择的方法来访问它。

    这种方法还允许灵活性,因为您的数据访问需求可能会发生变化。例如,如果你被问到这样一个问题:“每个州有多少金发司机被开了超速罚单?”。SQLite数据库可以通过一个命令来实现这一点,而其他解决方案可能需要返回一组数据,然后需要对这些数据进行解析、分组和设置以供输出。

        8
  •  -5
  •   Mathieu Longtin    17 年前

    如果你不想处理ORM,我经常有这样的代码:

    my (@columns,@tables,@wheres,@order_bys,@values);
    
    ... # Add value to those variables as needed, using push.
    ... # use ? for variables to be quoted
    
    # Build SQL statement
    my $sql = "select ".join(",",@columns).
        " from ".join(",",@tables).
        " where ".join(" and ",@wheres).
        " order by ".join(",",@order_bys);
    
    my $sth = $dbh->prepare($sql);
    $sth->execute(@values);
    

    简单,不需要ORM,非常可定制。另外,我总是觉得ORM对于我处理的大量数据来说太重了,但这是另一个问题。