为什么使用 SQL 构建器? Arel 诉 Sequel 诉 T-SQL

发布于 2024-10-17 00:48:31 字数 2209 浏览 5 评论 0原文

我试图了解通过面向对象的构建器 DSL 构建 SQL 与参数化原始 SQL 字符串相比的好处。在以三种方式研究/实现相同的查询之后,我注意到原始 SQL 是迄今为止最容易阅读的。这就引出了一个问题:“为什么要跳进圈子?”为什么不直接声明并使用原始 SQL?

以下是我的想法:

首先,我想它使 SQL 更加可移植,因为任何带有适配器的数据库都可以使用它。我想这才是大事吧?不过,大多数 T-SQL 难道不是大多数数据库都能理解的吗?

其次,它提供了一个可以重用的查询对象——作为其他查询、命名范围链等的基础。

通过构建 SQL 而不是声明它,您实现的主要投资回报是什么?

def instances_of_sql(ttype_id) #raw sql
  ttype_id = get(ttype_id).try(:id)
  ti   = get('tmdm:type-instance')
  inst = get('tmdm:instance')
  type = get('tmdm:type')

  self.class.send :sanitize_sql, [%{
    SELECT t.*
    FROM associations a
    JOIN roles type    ON type.association_id = a.id AND type.ttype_id = ?
    JOIN roles inst    ON inst.association_id = a.id AND inst.ttype_id = ?
    JOIN topics t      ON t.id = inst.topic_id
    WHERE a.topic_map_id IN (?)
    AND a.ttype_id    = ?
    AND type.topic_id = ?
  }, type.id, inst.id, self.ids, ti.id, ttype_id]
end

def instances_of_sql(ttype_id) #sequel
  ttype_id = get(ttype_id).try(:id)
  ti = get('tmdm:type-instance')
  ir = get('tmdm:instance')
  tr = get('tmdm:type')

  DB.from(:associations.as(:a)).
    join(:roles.as(:tr), :tr__association_id => :a__id, :tr__ttype_id => tr[:id]).
    join(:roles.as(:ir), :ir__association_id => :a__id, :ir__ttype_id => ir[:id]).
    join(:topics.as(:t), :t__id => :ir__topic_id).
    where(:a__topic_map_id => self.ids).
    where(:a__ttype_id => ti[:id]).
    where(:tr__topic_id => ttype_id).
    select(:t.*).sql
end

def instances_of_sql(ttype_id) #arel
  ttype_id = get(ttype_id).try(:id)
  ti   = get('tmdm:type-instance')
  inst = get('tmdm:instance')
  type = get('tmdm:type')

  #tables
  t    = Topic.arel_table
  a    = Association.arel_table
  tr   = Role.arel_table
  ir   = tr.alias

  a.
    join(tr).on(tr[:association_id].eq(a[:id]),tr[:ttype_id].eq(type[:id])).
    join(ir).on(ir[:association_id].eq(a[:id]),ir[:ttype_id].eq(inst[:id])).
    join(t).on(t[:id].eq(ir[:topic_id])).
    where(a[:topic_map_id].in(self.ids)).
    where(a[:ttype_id].eq(ti[:id])).
    where(tr[:topic_id].eq(ttype_id)).
    project('topics.*').to_sql
end

我非常欣赏命名作用域,并了解链接它们的好处。我不担心通过模型访问相关记录。我纯粹是在谈论构建一个复杂的查询。

I'm trying to understand the benefits of building SQL via an object-oriented builder DSL vs. parameterizing a raw SQL string. After researching/implementing the same query three ways, I notice that the raw SQL is by far the easiest to read. This begs the question, "why jump through a hoop?" Why not just declare and use raw SQL?

Here's what I've come up:

First, I guess it makes the SQL more portable as it could then be utilized by any DB with an adapter. I guess this is the biggie, right? Still, isn't most T-SQL intelligible to most databases?

Second, it provides a query object that can be reused--as the basis for other queries, named-scope chaining, etc.

What's the main return on investment you realize by building your SQL instead of declaring it?

def instances_of_sql(ttype_id) #raw sql
  ttype_id = get(ttype_id).try(:id)
  ti   = get('tmdm:type-instance')
  inst = get('tmdm:instance')
  type = get('tmdm:type')

  self.class.send :sanitize_sql, [%{
    SELECT t.*
    FROM associations a
    JOIN roles type    ON type.association_id = a.id AND type.ttype_id = ?
    JOIN roles inst    ON inst.association_id = a.id AND inst.ttype_id = ?
    JOIN topics t      ON t.id = inst.topic_id
    WHERE a.topic_map_id IN (?)
    AND a.ttype_id    = ?
    AND type.topic_id = ?
  }, type.id, inst.id, self.ids, ti.id, ttype_id]
end

def instances_of_sql(ttype_id) #sequel
  ttype_id = get(ttype_id).try(:id)
  ti = get('tmdm:type-instance')
  ir = get('tmdm:instance')
  tr = get('tmdm:type')

  DB.from(:associations.as(:a)).
    join(:roles.as(:tr), :tr__association_id => :a__id, :tr__ttype_id => tr[:id]).
    join(:roles.as(:ir), :ir__association_id => :a__id, :ir__ttype_id => ir[:id]).
    join(:topics.as(:t), :t__id => :ir__topic_id).
    where(:a__topic_map_id => self.ids).
    where(:a__ttype_id => ti[:id]).
    where(:tr__topic_id => ttype_id).
    select(:t.*).sql
end

def instances_of_sql(ttype_id) #arel
  ttype_id = get(ttype_id).try(:id)
  ti   = get('tmdm:type-instance')
  inst = get('tmdm:instance')
  type = get('tmdm:type')

  #tables
  t    = Topic.arel_table
  a    = Association.arel_table
  tr   = Role.arel_table
  ir   = tr.alias

  a.
    join(tr).on(tr[:association_id].eq(a[:id]),tr[:ttype_id].eq(type[:id])).
    join(ir).on(ir[:association_id].eq(a[:id]),ir[:ttype_id].eq(inst[:id])).
    join(t).on(t[:id].eq(ir[:topic_id])).
    where(a[:topic_map_id].in(self.ids)).
    where(a[:ttype_id].eq(ti[:id])).
    where(tr[:topic_id].eq(ttype_id)).
    project('topics.*').to_sql
end

I totally appreciate named scopes and see how chaining them can be beneficial. I'm not worried about accessing related records via a model. I'm purely talking about building a complex query.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

狂之美人 2024-10-24 00:48:31

@Kyle Heironimus 给出的 Nick Kallen 对 Arel 的想法的链接有这样一行:

您会注意到派生的使用
子选择中的表。这是
在我看来,太可怕了。仅限高级
SQL 程序员知道如何写这个
(我在工作中经常问这个问题
我从未见过的采访
任何人都做对了)。还有它
应该不难!

好吧,Kallen 将其归因于 SQL 中组合下缺乏闭包。在某些情况下这可能是正确的,但我的经验更为平淡——大多数开发人员在 SQL 方面都很糟糕。他们只知道最基本的东西,当他们试图用基于集合的语言寻找过程解决方案时,这些基本的东西被误用了。我不得不在我所在的一家公司与所有其他开发人员争论数据库采用 3NF 的好处,但他们就是不明白。有才华的人(他们中的大多数:),但对 SQL 或数据库一无所知。

将其置于 C# 或 Ruby 或 Python 中<插入选择的语言>开发人员又高兴了。他们可以坚持过程/面向对象的思维并生成对他们来说看起来不错的代码。

我知道这不会为我赢得任何选票,可能恰恰相反,但这是我的观点。顺便说一句,阿雷尔看起来很有趣。


作为我上面评论的补充,六个月过去了,并且在这段时间里大量使用了 Sequel 库,我可以说它确实是一件美丽的事情,现在我觉得我会使用它 领先于使用直接 SQL。它不仅非常强大并且允许我做简单和高级的事情而无需太多的头疼(总会有一些)它可以输出它所使用的 SQL,并且如果我感觉我需要。

这并不会以任何方式否定我对大多数开发人员对 SQL 理解的评论,(最近一位与其他人进行演讲的开发人员告诉我,规范化是存储空间昂贵时代的遗物......哦天哪!)只是Sequel库的开发显然是由那些真正了解数据库的人完成的。如果您了解 SQL 和数据库设计等,那么它会更快地为您提供更多功能。我不能说我使用过的其他 ORM 是一样的,但也许其他人会有不同的想法。

The link that @Kyle Heironimus gave to Nick Kallen's thoughts on Arel had this line:

You'll note the use of the derived
table in the subselect. This is
terrible, in my opinion. Only advanced
SQL programmers know how to write this
(I’ve often asked this question in job
interviews I’ve never once seen
anybody get it right). And it
shouldn’t be hard!

Well, Kallen puts this down to the lack of closure under composition in SQL. That may be true in some cases, but my experience is much more prosaic - that most devs are terrible at SQL. They only know the most basic things, these basic things are mis-used as they try to search for procedural solutions in a set based language. I had to argue the benefits of the database being in 3NF at one company I was at, with all the other devs, they just didn't get it. Talented guys (most of them:), but no clue about SQL or databases.

Put it in C# or Ruby or Python <insert language of choice> and the devs are happy again. They can stick with procedural/OO thinking and produce code that looks good to them.

I know this won't earn me any votes, probably quite the opposite, but it's my view. Arel looks interesting BTW.


As an addendum to the comments I've made above, over six months on and having used the Sequel library a lot during that time, I can say that it is indeed a beautiful thing, and now I feel that I would use it ahead of using straight SQL. Not only is it incredibly powerful and allow me to do simple and advanced things without too much head scratching (there'll always be some) it can output the SQL it has used, and it will also allow me to drop down into SQL if I feel I need to.

This doesn't in any way nullify my comments about most dev's understanding of SQL, (I was recently told, by a dev that gives talks to others, that normalisation was a relic of a time when storage space was expensive... oh dear!) just that the development of the Sequel library has obviously been done by those who really understand databases. If you know SQL and db design etc then it gives you more power more quickly. I can't say the same of the other ORM's I've used, but perhaps others would think differently.

月亮是我掰弯的 2024-10-24 00:48:31

你已经很清楚原因了。

以下是 Arel 的创建者的想法。

You have pretty much hit on the reasons already.

Here are thoughts from the creator of Arel.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文