从配置表中检索值以在查询中使用

时间:2018-01-28 19:41:48

标签: sql database oracle ms-access

我正在考虑如选项2 here中那样设置配置表,其中每个配置选项有一行(或者#34;事实"),其中包含事实的名称和事实的价值。

将此信息合并到另一个具有一个或多个连接的查询中的适当/最佳方法是什么?请注意,我明确没有考虑过程SQL或ORM系统;仅限纯SQL。

例如,请考虑这些表格,以设计一个例子:

SELECT tbl_projects.Project_ID, tbl_projects.Name, 
    (tbl_projects.Revenue - tbl_employees.Salary) AS Gross_Profit
FROM tbl_projects INNER JOIN tbl_employees ON 
    tbl_projects.Manager_ID = tbl_employees.Employee_ID;

Manager_ID链接到tbl_employees以获取管理项目的员工。每个项目只有一个经理,但员工可以管理多个项目。为了这个例子,Option_Value是数字/双。

忽略项目中其他员工的成本等,让我们找出每个项目的毛利作为收入 - [经理]薪水:

SELECT tbl_projects.Project_ID, tbl_projects.Name, 
    (tbl_projects.Revenue - tbl_employees.Salary) AS Gross_Profit,
    tbl_facts.Option_Value * (tbl_projects.Revenue - tbl_employees.Salary) AS Net_Profit
FROM 
    (tbl_projects INNER JOIN tbl_employees ON 
        tbl_projects.Manager_ID = tbl_employees.Employee_ID),
    tbl_facts
WHERE tbl_facts.Option_Name="Tax Rate";

够容易。现在让我们说我们知道利润的税率是20%,所以我们将("税率",0.2)存储在tbl_facts(以及其他事实)中。我们想找出净利润。问题是,目前还没有明确的方法将这些信息加入到目前为止。

我见过/考虑的选项:

  1. 使用tbl_facts进行笛卡尔/交叉连接。但是,有些SQL不支持这种情况,或者至少不支持它(有时它可以工作,有时它不会)。

    SELECT tbl_projects.Project_ID, tbl_projects.Name, 
        (tbl_projects.Revenue - tbl_employees.Salary) AS Gross_Profit,
        cdbl(dlookup("Option_Value","tbl_facts","Option_Name='Tax Rate'")) *
            (tbl_projects.Revenue - tbl_employees.Salary) AS Net_Profit
    FROM 
        tbl_projects INNER JOIN tbl_employees ON 
            tbl_projects.Manager_ID = tbl_employees.Employee_ID;
    
  2. 某些版本的SQL支持查找功能(例如,MS Access< dlookup())。但是,这并不是很好,因为你必须将值转换为你想要的类型,我怀疑它会有性能损失。

    SELECT tbl_projects.Project_ID, tbl_projects.Name, 
        (tbl_projects.Revenue - sq_employees.Salary) AS Gross_Profit,
        sq_facts.Option_Value * (tbl_projects.Revenue - sq_employees.Salary) AS Net_Profit
    FROM 
        tbl_projects INNER JOIN 
            (SELECT 1 AS Join_Col, tbl_employees.* FROM tbl_employees) AS sq_employees ON 
            tbl_projects.Manager_ID = sq_employees.Employee_ID),
        INNER JOIN
            (SELECT 1 AS Join_Col, Option_Value FROM tbl_facts WHERE Option_Name="Tax Rate") AS sq_facts ON
                sq_employees.Join_Col = sq_facts.Join_Col;
    
  3. 使用子查询添加一个人为的"加入列"。这似乎是一个可怕的选择。

    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet var secondView: UIView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            print("Main View:")
            print("Width: \(self.view.frame.width), Height: \(self.view.frame.height)")
    
            print("Second View:")
            print("Width: \(self.secondView.frame.width), Height: \(self.secondView.frame.height)")
    
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    
    }
    
  4. 从这样的配置表中获取此信息的首选方法是什么?这是上面的选择之一,还是我还没考虑过的其他选择?

3 个答案:

答案 0 :(得分:3)

通常的方法是在SELECT语句中使用简单的子查询:

SELECT (SELECT TOP 1 Option_Value As Tax_Rate FROM tbl_facts WHERE Option_Name='Tax Rate'), other_columns
FROM tbl_projects

让我们快速回顾一下选项:

  1. 一个简单的子查询。这可以充分利用任何现有索引,没有类型转换,没有其他问题。它只能返回一个列,因此对于多列可能不太优化,如果返回多行,则会遇到错误
  2. 交叉加入。与单个子查询相比,这可以返回多个列。它也可以利用任何现有的索引,并且没有类型转换,因此没有理由降低它的效率。
  3. 查找功能。对于大多数场合来说,这是一个糟糕的计划,但主要是在UPDATE查询中的Access中,您需要有一个可更新的子查询,并且可以使用DLookUp。
  4. 所以真正的答案是:它取决于。简单的子查询是最明显的候选者,当您需要多个值时,交叉连接很有用,而DLookUp对于避免使用不可更新的查询时出错很有用。

    当然,当我们将VBA引入比较时,它会变得更加复杂。使用VBA的另一种方法是使用一个Options类,它在数据库打开时初始化,并且可以缓存选项查找。

答案 1 :(得分:2)

  

“你必须将值转换为你想要的类型,我怀疑会有性能损失”

您有一个存储为字符串的数值,并且您希望对其进行算术运算。因此,您必须将其转换为数字:您可以将数据转换显式化,因为您将以任何方式执行此操作。

Oracle确实在预测中支持标量游标。就个人而言,我会在内联视图中以CROSS JOIN的形式进行此操作,在我看来,这是最具表现力的做事方式。您的里程可能会有所不同。

SELECT tbl_projects.Project_ID
       , tbl_projects.Name
       , (tbl_projects.Revenue - tbl_employees.Salary) AS Gross_Profit
       , tf.tax_rate * (tbl_projects.Revenue - tbl_employees.Salary) AS Net_Profit
FROM tbl_projects 
  INNER JOIN tbl_employees ON 
    tbl_projects.Manager_ID = tbl_employees.Employee_ID
  CROSS JOIN ( select to_number(Option_Value) as tax_rate
               from tbl_facts
               WHERE Option_Name='Tax Rate') tf
;

如果您要存储键值对,请使用索引组织表。它基本上是一个索引查找,所以为什么要烦扰桌子呢?至少对Oracle而言。 Find out more.

答案 2 :(得分:2)

在开始编写查询之前修复数据模型。OPTION_VALUE拆分为多个列,例如OPTION_NUMBEROPTION_STRINGOPTION_DATE

永远不要将数据存储在错误的类型中。这是一场等待发生的灾难。名为OPTION_VALUE的单个列可能看起来像一个简单的解决方案,但您将在以后通过间歇性错误或复杂查询为此付费。

the top answer in the question you referenced建议这样做。我也在this post中写过这篇文章。

总结一下,在EAV模型中使用多个列:

  1. 提高性能 - Oracle可以更好地理解数据并制定更好的计划
  2. 减少存储 - 特定数据类型更好地优化以存储数据
  3. 简化验证 - 验证数字和日期并不像您想象的那么简单
  4. 提高类型安全性 - Oracle不必按写入的顺序运行SQL语句。此SQL语句很危险,并且会间歇性地失败:

    select *
    from
    (
        select to_number(option_value) tax_rate
        from tbl_facts
        where option_name='tax rate'
    )
    where tax_rate >= 1
    
  5. 无论如何不是更难。实际上,在使用它们之前,您总是会知道列的类型。