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

时间:2009-01-26 16:35:51

标签: sql perl oop

我目前负责一个似乎与数据库非常亲密的流程。我的程序/脚本/框架的目标是从不同的数据源中实现统一。使用依赖注入的形式,我的进程在很高的水平上工作正常。每个数据源类型的实现都隐藏在最高级别的业务抽象中。大。我的问题是两个。

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

2)如果我有一个功能(构建SQL?),我是否将整个业务对象传入,然后在最后一分钟对它们进行字符串化?或者我是否尽早将它们串联起来,只让我的函数处理它所需要的东西,而不是渲染对象本身?

编辑:虽然我不怀疑ORM的重要性,但我认为我们还没有进入ORM领域。想象一下,美国,国家和虚构联盟的棒球数据都以不同的格式存储,具有不同的标准化水平。读取这些数据源并将它们放在一个统一的规范化池中是我的过程的工作。我觉得在我的过程之后发生了对这些物体采取行动的ORM空间。如果你愿意,我是一名数据看门人。由于缺少我创建的统一池,因此基本上没有业务对象可以采取行动。

编辑^ 2 :引起我的注意,也许我没有详细描述问题空间。这是一个例子。

想象一下,你必须建立一个美国所有罪犯的主数据库。贵公司的服务是销售一种产品,它以最清晰,统一的格式提供对这些数据的访问。

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

我的项目的目的是为50个州中的每个州制定一个“驱动程序”,并确保该过程的最终产品是一个完美的关系模型中的犯罪分子的主数据库。一切都正确键入,架构形状完美等等。

8 个答案:

答案 0 :(得分:9)

您想查看 Fey 。几个月前我开始在工作中使用它,虽然由于年龄的原因,实施仍然有严峻的角落,但它背后的想法是可靠的。 F.ex.,从手册中略微改编一下:

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 );
}

这将添加从usergroup的内部联接以及WHERE条件。瞧,你可以在主程序中写下以下内容:

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

但是这个restrict_with_group函数适用于任何查询,它具有group表的外键!要使用它,你传递要限制的查询以及要应用限制的表,以及要限制它的组ID。

最后你说$q->sql( $dbh ),你会得到一串代表你在$q对象中建立的查询的SQL。

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

答案 1 :(得分:7)

请不要编写自己的ORM。使用类似DBIx::Class的内容。

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

答案 2 :(得分:5)

如果您想要一个ORM,但是您想要从没有直接字符串操作/连接的位组装SQL,请查看Fey,这可能会执行您想要的操作

更新:亚里士多德Pagaltzis的答案要好得多。他实际上给出了Fey看起来像以及它如何帮助的例子。

答案 3 :(得分:1)

从纯粹的编码角度来看 - 你手上有一长串复杂的代码。你不喜欢它。为什么?我只能假设那里有一些代码重复。否则,有什么不喜欢的?因此,重构它以消除重复...我知道它听起来很陈旧,但由于你没有发布代码,因此很难更具体。可能有一个对象具有from,where和insert子句的方法,以便SQL的基础结构不重复?我确实不知道该怎么做,但消除重复是关键。

答案 4 :(得分:1)

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

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

采用50个州的想法,你无法真正摆脱50个“提取”过程,希望有一个共享例程库。我一次攻击一个输入源的问题,重构,因为我添加了新的,但要小心封装可变部分,以便我知道当供应商更改其格式时需要做出哪些更改。

“变换”部分不应过于繁重:只需拿走你得到的东西并准备输出。

答案 5 :(得分:0)

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

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

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

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

答案 6 :(得分:0)

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

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

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

答案 7 :(得分:-5)

如果你不想处理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对于我正在处理的数据量太重了,但这是另一个主题。