【问题标题】:Framework to Avoid Writting Repetitive Joins in SQL/Select Statements?避免在 SQL/Select 语句中编写重复连接的框架?
【发布时间】:2011-06-08 23:31:29
【问题描述】:

假设表 TA 中的每条记录都指向表 TB 中的一条记录,而该记录又指向表 TC 中的一条记录。如果我要编写一个 select 语句来从表 TA 中检索一些结果,并且想要包含一个使用在来自表 TC 的记录上定义的远程属性的条件,那么我必须编写一堆连接语句并记住名称将这些表链接在一起的主键和外键。嗯,这很容易做到,但是也很无聊,尤其是当你编写类似的连接来解决不同查询中的类似问题时......

使用对象表示法来访问子记录/属性会更容易,或者像这样:

"TA.TB.TC.attribute"

我什至更喜欢使用路径表示法:

"TA/TB/TC/attribute"

当然,为了让类似的东西正常工作,应该有一种方法可以在某处或类似的地方预先定义表的关系(伪代码):

table TA(
    has one TB as "B"
    on (TA.fkey = TB.pkey)
)

table TB(
    has one TC as "C"
    on (TB.fkey = TC.pkey)
)

所以我们可以这样写一个选择指令:

select * from TA where B/C/attribute = "something"

这会被解释器转换成:

select * from TA 
join TB as B on (TA.fkey = TB.pkey)
join TC as C on (TB.fkey = TC.pkey)
where C.attribute = "something"

此外,甚至还有一种方法可以为我在这里所说的“远程属性”创建“快捷方式”。示例:

   table TA(
    has one TB as "B"
    on (TA.fkey = TB.pkey);
   )

# define a "shortcut"
TA.C_attribute = TA/B/C/attribute;

然后 C_attribute 将作为 TA 的列存在,但只是虚拟的而不是实际的;尽管如此,它会在查询中变得可见和可访问:

select * from TA where C_attribute = "something"

这将强制解析器/解释器检查用户预定义的所有规范,然后使用所有这些连接正确地重写 SQL。

那么,有没有在 PHP 中执行此操作的工具/框架?我不是指教义——它没有涵盖我在这里经历的所有这些要求。

【问题讨论】:

  • 像 Doctrine 这样的 ORM 缺少哪些功能?据我所见,它可以满足您的所有要求。
  • 那么您能否将我链接到他们解释您将如何执行诸如定义“远程属性的快捷方式”之类的部分?这就是我所需要的。谢谢。

标签: php sql frameworks join


【解决方案1】:

好吧,如果您想在不使用 ORM 的情况下摆脱连接,则必须摆脱关系数据库引擎。毕竟,这个名字是不言自明的。它是关系型的,因此您必须将记录连接在一起才能创建关系。

您可能正在寻找的可能是一个对象数据库。 Document-oriented databases,也可以建立在object databases之上,也可以满足你的需求。

我使用MongoDB。它有一个PHP extension,并且有很多有用的特性(最显着的是可扩展性和分片)。

在 Mongo 中,您存储文档,这些文档基本上是 JSON 对象。考虑一个“mydb”数据库和一个集合“mycoll”(如果你喜欢,集合类似于表):

> x = {"foo":"bar", "hello":"world", "some_list":["a","b","c"]}
> db.mycoll.save(x)                             // save the object 'x'
> db.mycoll.find()                              // search the collection
{ "_id" : ObjectId("4df0190b01fdc88b103d3354"), "foo" : "bar", "hello" : "world", "some_list" : [ "a", "b", "c" ] }

如您所见,它也支持嵌套对象和数组。此外,它还支持对其他对象的引用:

> y = {"some_ref" : new DBRef("mycoll", x._id)} 
> db.mycoll.save(y);                            // save the object 'y'
> x.foo = "something has changed!"              // change the object 'x'
> y.some_ref.fetch().foo                        // query 'y' for object 'x'
something has changed!

是的,它基本上是一个 JavaScript shell,这意味着您还可以创建函数:

> myfunc = function(foo) {
... foo.foo = "something else has changed!";
... }
> myfunc(x)
> x
{
    "foo" : "something else has changed!",
    "hello" : "world",
    "some_list" : [
        "a",
        "b",
        "c"
    ],
    "_id" : ObjectId("4df0190b01fdc88b103d3354")
}

当然,它不会取代关系数据库,但对于我最近从事的许多项目来说,它非常适合。尤其是那些有自定义字段和元数据的,因为它是无模式的。

【讨论】:

  • 这个“对象数据库”和“面向文档的数据库”对我来说是全新的东西,我会检查一下。但是我想知道它是否与关系引擎一样优化。
  • @fabio:这取决于什么,但我可以向你保证,MongoDB 非常快。 See this article,或者自己试试。 :)
【解决方案2】:

数据库问题有点微妙,您可能需要重新考虑一下。

说TA表中的每条记录 指向表中的一条记录 TB 又正好指向一个 记录在表 TC 中。

create table TA (
  id integer primary key,
  results char(1) not null
);

create table TB (
  id integer primary key references TA (id)
);

create table TC (
  id integer primary key references TB (id),
  remote_attr char(1)
);

insert into TA values
(1,'A'),(2,'B'),(3,'C');

insert into TB values
(1),(2),(3);

insert into TC values
(1,'a'),(2,'a'),(3,'b');

如果我要写一个选择语句 从表 TA 中检索一些结果 并希望包括一个条件 使用在 a 上定义的远程属性 记录来自表 TC,然后我 得写一堆join 陈述并记住名称 的主键和外键 将这些表链接在一起。

是的,您需要能够查找键的名称,但您只需要一个连接。

select TA.id, TA.results
from TA
inner join TC using (id)
where remote_attr = 'b';

id   results
--
3    C

较早的技术需要应用程序代码在所有表中追踪指针。关系模型(和 SQL)允许您直接加入公共值。

我其实不介意写更明确的版本。

select TA.id, TA.results
from TA
inner join TC on (TC.id = TA.id and TC.remote_attr = 'b')

您实际上不需要记住键的名称。在工作中,我的操作数据库有一千多个表。我不会尝试记住所有键的名称;我只是查找它们。 (我也不会尝试记住所有表和视图的名称。)

微妙的数据库问题与以下语法有关:“TA.TB.TC.attribute”。该语法只会给你一个等值连接的结果,然后只能通过等效的追逐指针。 SQL 连接语法比简单的等连接更具表现力。

如果你只能用这种语法表达等值连接——而不是所有的连接条件——为什么要使用它?

【讨论】:

  • 您已经很好地说明了等值连接的局限性。但是,在您的示例中,假设我们只处理一对一类型的关系,您设计了一个与“使用”功能很好地配合的数据库模式。当发布此问题时,我想到的实际情况与一对多关系有关;例如:TB 中的一条记录在表 TA 中可以有许多相关记录,但 TA 只能指向表 TB 中的一条记录。 TB 和 TC 也是如此。在这种情况下,您建议的架构不适合。
  • @fabio:如果您要编辑问题以反映一对多的要求——或者更好的是,包括示例 DDL 和 INSERT 语句——我将编辑我的答案。不过,我认为这不会改变 equi-join 问题。
  • 没关系,我不打算编辑我​​的问题。我将尝试更准确地回答关于 SO 的下一个问题。谢谢。
猜你喜欢
  • 2019-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 2016-06-21
  • 2012-10-18
  • 2021-09-27
相关资源
最近更新 更多