【问题标题】:SQL schema help for table with two semi-related join tables具有两个半相关连接表的表的 SQL 模式帮助
【发布时间】:2013-10-12 21:33:51
【问题描述】:

我在以适合数据库的方式表示项目数据时遇到问题。为了提供帮助,我为示例设置了一个 SQL Fiddle:http://sqlfiddle.com/#!2/6d01c2/14/0

简单地说,我有三个表:Fund、FundReturn 和 FactorReturn。基金通过 FundReturn 有许多月度回报。基金也有一个地区(如美国或外国),这些地区有所谓的 FactorReturn。因此,FactorReturn 与 Fund 的关系不是直接的,而是通过 Fund 所属的区域。

**Fund**
fund_symbol  | varchar(5)    | PRI
region_key   | varchar(255)  |

**FundReturn**
fund_symbol  | varchar(255)  | PRI
return_month | int(10)       | PRI
return_value | decimal(5,4)  | 

**FactorReturn**
region_key   | varchar(255)  | PRI
factor_key   | varchar(255)  | PRI
return_month | int(10)       | PRI
return_value | decimal(5,4)  | 

目标是提取基金在给定时间段内的回报以及相应的因子回报以进行一些分析。最终的表示看起来像这样:

fund_symbol | month  | fund_return | factor_ret_1 | factor_ret_2 | factor_ret_3
VTI         | 201001 | 0.0100      | 0.0200       | -0.0100      | 0.0000
VTI         | 201002 | 0.0500      | 0.0300       |  0.0300      | 0.0010
VTI         | 201003 | 0.0300      | 0.0100       | -0.0200      | 0.0020

此外,我正在使用 PHP Doctrine ORM 并已映射数据以便能够使用如下方法:

$fund = $em->find('VTI');
$fund->getFundReturns();   // Would return array with FundReturn objects.
$fund->getFactorReturns(); // Would return array with FactorReturn objects.

问题


由于数据的性质,尝试在一次查询中同时查询基金收益和因子收益会成倍增加返回的行数。一旦我尝试在 ORM 中使用它,这种情况就会加剧,因为不可能构建具有广泛日期范围的对象图。使用多个查询是我目前的解决方案,但是在使用 ORM 时这很棘手,因为从我的代码中错误地访问关系可能会触发大量 SQL 查询。

http://sqlfiddle.com/#!2/6d01c2/14/0 的查询显示了仅查询 12 个月时如何返回 432 行。

问题


所以我的问题是:

  1. 有没有更好的方法在数据库中表示这些数据?
  2. 在 SQL 中查询数据的正确方法是什么?在 ORM 中?

【问题讨论】:

    标签: php sql join orm schema


    【解决方案1】:

    首先,任何解决方案无疑都会以使用 'JOIN's 结束,因此我将创建所有外键整数值。这将使您的表关系更快,存储和更新速度更经济。因此,例如,基金表将有一个整数唯一 ID(例如 1)和一个符号(自然键,例如 VTI) 你可以在这里阅读更多: Surrogate vs. natural/business keys

    此外,您似乎正在走实体属性值路线,这已被大量讨论。 Entity Attribute Value Database vs. strict Relational Model Ecommerce

    这种实体属性值模型查询起来很复杂,但让用户可以灵活地添加自己的因子返回键。

    其次,如果您沿着这条路线走,您可能会在大多数 ORM 中难以查询。我会尝试手动构建查询,如下所示:

        SELECT f.*
          ,fr.return_month AS fund_return_month
          ,fr.return_value AS fund_return
          ,hml.return_value AS hml
          ,mkt.return_value AS mkt
          ,smb.return_value as smb
    
        FROM Fund f
        INNER JOIN FundReturn fr ON f.fund_symbol = fr.fund_symbol
    
        LEFT JOIN FactorReturn hml 
        ON f.region_key = hml.region_key
        AND hml.factor_key = 'hml'
        AND hml.return_month = fr.return_month
    
        LEFT JOIN FactorReturn mkt 
        ON f.region_key = mkt.region_key
        AND mkt.factor_key = 'mkt'
        AND mkt.return_month = fr.return_month
    
        LEFT JOIN FactorReturn smb 
        ON f.region_key = mkt.region_key
        AND smb.factor_key = 'smb'
        AND smb.return_month = fr.return_month
    
        WHERE f.fund_symbol = 'VTI'
        AND fr.return_month BETWEEN 201001 AND 201012
        AND hml.return_month BETWEEN 201001 AND 201012;
    

    【讨论】:

    • 如果计划只有 5 个因子,我最好对表格进行非规范化并将每个因子作为一列吗?或者,一些设计建议在实体-属性-值情况下使用类表继承,但对于这种类型的数据是否错误/过度杀伤?
    • 每个基金的这些因素(因素键)是否会有所不同,它们是否大部分都为NULL?如果是这样,EAV(实体值属性)可能是最好的前进方式。用户将无法使用类表继承添加自定义因子键。我个人不喜欢 EAV,因为它使查询和报告成为一场噩梦。
    • 我终于能够测试各种策略,我认为您的回答涵盖了一切。多重 LEFT JOIN 策略正是我在 SQL 端寻找的,但通过 ORM 使用它并不简单。通过 Doctrine 的 Native SQL 之类的自定义水化或 PHP 方面的一些进一步选择似乎是必要的。谢谢!
    猜你喜欢
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    相关资源
    最近更新 更多