SQL Server为查询中的每一行计算变量

时间:2014-03-14 20:48:39

标签: sql sql-server-2008 variables local-variables

我有一个包含发票的大型数据集,我需要执行一些货币转换。获取数据需要很多加入,但基本的想法是,一张桌子的单独发票费用是美元,而另一张桌子的收费是本地货币,但没有。发票上的个别费用以当地货币提供,因此我需要通过将当地货币总额除以美元总额来计算有效汇率,然后将费用乘以此费率。到目前为止,我已成功完成了以下单条记录:

DECLARE @USD_Total float, @LOC_Total float, @FX_Rate float

SET @USD_Total = (Select SUM (InvoiceTable.USD_ChargeAmount)
FROM 
{Some Joins}
WHERE InvoiceID in ('1234567')
Group By InvoiceTable.InvoiceID)

SET @LOC_Total = (Select LocalInvoiceTable.ChargeTotal
FROM
{Some Joins}
WHERE InvoiceID in ('1234567')
Group By LocalInvoiceTable.InvoiceID

SET @FX_Rate = @LOC_Total / @USD_Total

SELECT
InvoiceTable.InvoiceID
    SUM(CASE InvoiceTable.ChargeCode when 'TYPE A' THEN InvoiceTable.USD_ChargeAmount ELSE 0 END)*@FX_Rate As Type_A,
    SUM(CASE InvoiceTable.ChargeCode when 'TYPE B' THEN InvoiceTable.USD_ChargeAmount ELSE 0 END)*@FX_Rate As Type_B,
    SUM(CASE InvoiceTable.ChargeCode when 'TYPE C' THEN InvoiceTable.USD_ChargeAmount ELSE 0 END)*@FX_Rate As Type_C
FROM
{Some Joins}
WHERE InvoiceID in ('1234567')
Group By LocalInvoiceTable.InvoiceID

所以这很好用,但我需要复制数千个发票ID。如何在没有WHERE子句的情况下完成此操作?我是这方面的新手,所以非常感谢任何帮助。我正在使用Microsoft SQL Server 2008。

3 个答案:

答案 0 :(得分:0)

是的,你可以。不,你不应该

这只是一个糟糕的编程习惯。您应该分离并隔离程序的不同部分,以便日后维护(想想下一个程序员!)

SQL是一种数据持久化语言,它不是一种很好的过程语言

一定要使用SQL为报表等选择复杂的数据表示。但这里有的是嵌入到数据请求中的业务规则(逻辑)。这些业务规则还在哪里重复?

业务规则应存在于一个且仅存在一个地方,并从那里引用(参见DRY principle)。如果您将业务规则分散到整个系统中,它将很快变得不可维护。历史已经一次又一次地显示出来

使用您选择的服务客户端语言(Java,c#等等,任何体面的OO语言都可以)来完成此任务。对每个数据集一次性选择所有发票号。将整个事物包装在显式事务中,以便在需要时确保稳定的结果

答案 1 :(得分:0)

您不得将其视为程序性程序。 SQL是一种用于集合操作的语言。您需要考虑计算列和连接等术语。首先,您需要按发票计算美元总数,然后将其加入LocalInvoiceTable并除以ChargeTotal以获得外汇汇率。最后加入InvoiceTable并将每个单独的费用乘以外汇汇率。

with UsdTotal as
(
  select InvoiceID, sum(USD_ChargeAmount) as USD_ChargeTotal
  from InvoiceTable
  group by InvoiceID
)
, FxRate as
(
  select lit.InvoiceID, lit.ChargeTotal / ut.USD_ChargeTotal as Rate
  from LocalInvoiceTable lit
  inner join UsdTotal ut
    on ut.InvoiceID = lit.InvoiceID
)
select
  it.InvoiceId,
  it.USD_ChargeAmount as UsdValue,
  it.USD_ChargeAmount * fx.Rate as LocalCcyValue
from InvoiceTable it
inner join FxRate fx on fx.InvoiceID = it.InvoiceID

您可以在SQLFiddle中试用。请注意,外汇汇率每天都在变化。如果个人收费在不同的日子实现,则您无法获得有效的外汇汇率和平均值。因此,以当地货币计算的金额将与实际金额不同。

答案 2 :(得分:-1)

不要考虑个别行。您有一组行,因此请根据每个问题的集合进行思考。使用CTE,您可以封装每个部分(问题的每个部分的结果集),并将答案组合在其他CTE或最终查询中。

您的查询的以下修改显示3个CTE:

  1. 首先,我们为每个InvoiceID
  2. 获取美元价值
  3. 其次,我们获得每个InvoiceID的本地化值
  4. 第三,我们根据前两个聚合计算有效汇率,加入各自的InvoiceID
  5. 最后,在主查询中,我们使用有效汇率计算的结果

    ;WITH USD AS (
        SELECT InvoiceID, SUM(InvoiceTable.USD_ChargeAmount) AS [Total]
        FROM 
        {Some Joins}
        GROUP BY InvoiceTable.InvoiceID
    ),
    LOC AS (
        SELECT InvoiceID, SUM(LocalInvoiceTable.ChargeTotal) AS [Total]
        FROM
        {Some Joins}
        GROUP BY LocalInvoiceTable.InvoiceID
    ),
    FX AS (
        SELECT USD.InvoiceID,
               CONVERT(MONEY, LOC.Total) / CONVERT(MONEY, USD.Total) AS [Rate]
        FROM USD
        INNER JOIN LOC
                ON LOC.InvoiceID = USD.InvoiceID
    ),
    SELECT InvoiceTable.InvoiceID,
           SUM(CASE InvoiceTable.ChargeCode
                  when 'TYPE A' THEN InvoiceTable.USD_ChargeAmount
                  ELSE 0 END) * FX.[Rate] AS [Type_A],
           SUM(CASE InvoiceTable.ChargeCode
                  when 'TYPE B' THEN InvoiceTable.USD_ChargeAmount
                  ELSE 0 END) * FX.[Rate] AS [Type_B],
           SUM(CASE InvoiceTable.ChargeCode
                  when 'TYPE C' THEN InvoiceTable.USD_ChargeAmount
                  ELSE 0 END) * FX.[Rate] AS [Type_C]
    FROM InvoiceTable
    {Some Joins}
    INNER JOIN FX
            ON FX.InvoiceID = InvoiceTable.InvoiceID
    

    修改
    更多理想的方法是存储插入InvoiceTable和LocalInvoiceTable行时使用的汇率。您可以使用类似于:

    的表格
    InvoiceInfo
    (
      InvoiceID INT NOT NULL, -- PK, FK to InvoiceTable
      ExchangeRate SMALLMONEY NOT NULL,
      CurrencyCode CHAR(3) NOT NULL, -- USD, EUR, etc.
      InvoiceTotalUSD MONEY, -- optional
      InvoiceTotalLocal MONEY -- optional
    )
    

    我建议使用ISO 4217货币代码。两个InvoiceTotal *字段仅适用于您需要通过InvoiceID聚合而不需要计算"有效汇率"。

    在创建发票时存储信息会将您的查询简化为:

    SELECT InvoiceTable.InvoiceID,
           SUM(CASE InvoiceTable.ChargeCode
                  when 'TYPE A' THEN InvoiceTable.USD_ChargeAmount
                  ELSE 0 END) * II.[ExchangeRate] AS [Type_A],
           SUM(CASE InvoiceTable.ChargeCode
                  when 'TYPE B' THEN InvoiceTable.USD_ChargeAmount
                  ELSE 0 END) * II.[ExchangeRate] AS [Type_B],
           SUM(CASE InvoiceTable.ChargeCode
                  when 'TYPE C' THEN InvoiceTable.USD_ChargeAmount
                  ELSE 0 END) * II.[ExchangeRate] AS [Type_C]
    FROM InvoiceTable
    {Some Joins}
    INNER JOIN InvoiceInfo II
            ON II.InvoiceID = InvoiceTable.InvoiceID