【问题标题】:How can I add four calculated values to a result set from a Stored Proc (TSQL)?如何将四个计算值添加到存储过程 (TSQL) 的结果集中?
【发布时间】:2017-08-04 03:38:38
【问题描述】:

我需要扩展现有的存储过程(即,基于旧存储过程创建一个新的 SP)以包含一些额外的计算数据。需要四个额外的列来提供从 SP 的结果生成的报告:

简而言之,报告显示 MonthlySales,另外四个列填充了基于该行中指定的“Unit”的 Category.Subcategory 值的货币值(每个 Unit 在结果集中都有自己的行和报告)。

新列是四个类别:New.New、New.Assumed、Existing.Existing 和 Existing.Organic

因此,结果集中的每一行都包含单个 Unit 的数据,并且该 Unit 属于所报告时间段的 Category.Subcategory 值之一(IOW,这四个包中只有一个在每个包上都有一个值行)。

检索此数据涉及三个表:

MasterUnitProjSales,每个 Unit 都有一个“NewBiz”标志(如果为真,它属于两个值的“New”系列;否则,它属于两个值的“Existing”系列值;因此,Category.Subcategory 值的 Category 部分可以由该值确定)。

ReportingMonthlySales,每个 Unit/MemberNo 都有一个 MonthlySales(Money)字段,以及 Year 和 Month 数据。

CustomerCategoryLog,其中包含 Unit/MemberNo 对、Category 和 Subcategory 字段以及 BeginDate 和 EndDate 字段,用于记录 Unit/Member 所处的 Subcategory.Category开始/结束时间范围。

旧版 SP 的工作方式是将一年的数据存储到临时表中,然后将前一年的数据存储到第二个临时表中,然后将它们组合起来,然后返回。

有了这个额外的皱纹,我的想法(伪SQL,我不是[T]-SQL-head)是:

(“CombinedYears”是前两个临时表“CurrentYear”和“PriorYear”的合并表)

Select * from #CombinedYears CY,
NewNew = select MonthlySales from ReportingMonthlySales RMS where Category = 'New' and Subcategory = 'New' and
RMS.Unit = CY.Unit
left join CustomerCategoryLog CCL on RMS.Unit = CCL.Unit,
NewAssumed = select MonthlySales from ReportingMonthlySales RMS where Category = 'New' and Subcategory = 'Assumed' and
RMS.Unit = CY.Unit
left join CustomerCategoryLog CCL on RMS.Unit = CCL.Unit,
ExistingExisting = select MonthlySales from ReportingMonthlySales RMS where Category = 'Existing' and Subcategory = 'Existing' and
RMS.Unit = CY.Unit
left join CustomerCategoryLog CCL on RMS.Unit = CCL.Unit,
ExistingOrganic = select MonthlySales from ReportingMonthlySales RMS where Category = 'Existing' and Subcategory = 'Organic' and
RMS.Unit = CY.Unit
left join CustomerCategoryLog CCL on RMS.Unit = CCL.Unit

我知道这是错误的、笨拙的和笨拙的,但也许它可以帮助您理解表格之间的联系等。

注意:这个问题与我的问题 here 部分相关,但就提供上下文或背景信息而言,这可能没有帮助。

更新

我尝试了 Charles Bretana 的想法,但不得不对其进行调整以使其能够在 LINQPad 中“编译”。关于表格的确切性质,我可能误导了您。我将在下面包含它们,然后显示我在 LINQPad 中尝试的更改后的查询。

CustomerCategoryLog
-------------------
MemberNo (VarChar)
Unit (VarChar)
Custno (VarChar)
Category (VarChar)
Subcategory (VarChar)
BeginDate (DateTime)
EndDate (DateTime)
ChangedBy (VarChar)
ChangedOn (DateTime)

MasterUnitProjSales
-------------------
Unit (VarChar)
CYear (Int)
CSDirector (VarChar)
ProjectedSales (Money)
NewBiz (Int)
Category (VarChar) <= this is not directly connected to the "Category" I need, and should be ignored
Segment (VarChar)

ReportingMonthlySales
---------------------
AutoID (Int)
Unit (VarChar)
MemberNo (VarChar)
NumUnits (Int)
MonthlySales (Money)
CYear (Int)
Cmonth (Int)
CreateDate (DateTime)

以下内容(由我修改,但基于 Bretana 的)试图在 LINQPad 中生成一些数据,但需要“永远”:

DECLARE @CYear  INT
SET @CYear = 2016

DECLARE @Cmonth INT
SET @Cmonth = 4

Select  CSDirector,
        Category,
        Segment,
        r1.unit,
        NumUnits=isnull((Select sum(NumUnits) from ReportingMonthlySales where unit=r1.unit and cyear=r1.cyear and 
        cmonth = @Cmonth),0),    
        MonthSales=isnull((Select sum(MonthlySales) from ReportingMonthlySales where unit=r1.unit and cyear=r1.cyear 
and cmonth = @Cmonth),0.00), 
        YTDSales = (Select sum(MonthlySales) From  ReportingMonthlySales where unit=r1.unit and cyear=r1.cyear and 
cmonth <= @Cmonth),
        ProjSales =  (Select ProjectedSales from MasterUnitsProjSales where UNit = r1.Unit and Cyear=r1.cyear),
        YTDProjSales = (Select ProjectedSales from MasterUnitsProjSales where UNit = r1.Unit and Cyear=r1.cyear) / 12 
* @Cmonth,
        YTDBudgetPerc = (Select sum(MonthlySales) From  ReportingMonthlySales where unit=r1.unit and cyear=r1.cyear 
and cmonth <= @Cmonth) / 
            case when (Select ProjectedSales from MasterUnitsProjSales where UNit = r1.Unit and Cyear=r1.cyear) = 
0 then 1 else ((Select ProjectedSales from MasterUnitsProjSales where UNit = r1.Unit and Cyear=r1.cyear) / 12 * @Cmonth) end
into #CombinedYears2
From    MasterUnitsProjSales r1 
where r1.Cyear=@CYear
order by r1.NewBiz,r1.Unit

Select cy.*, 
   rms.MonthlySales newnew,
   rms.MonthlySales NewAssumed,
   rms.MonthlySales ExistingExisting,
   rms.MonthlySales ExistingOrganic
from #CombinedYears2 CY 
   left join ReportingMonthlySales rms
     on rms.Unit = cy.Unit
   join CustomerCategoryLog n
     on n.Category = 'New' 
        and n.Subcategory = 'New' 
        and n.Unit = cy.Unit
   left join CustomerCategoryLog a
      on a.Category = 'New' 
        and a.Subcategory = 'Assumed' 
        and a.Unit = CY.Unit
   left join CustomerCategoryLog e
      on e.Category = 'Existing' 
        and e.Subcategory = 'Existing' 
        and e.Unit = CY.Unit
   left join CustomerCategoryLog o
      on o.Category = 'Existing' 
        and o.Subcategory = 'Organic' 
        and o.Unit = CY.Unit

更新 2

在处理完其他事情/把它放在一边,然后回到它并重新攻击它,这是我的新伪sql:

DECLARE @Unit varchar(30); 
DECLARE @Year Int;
DECLARE @Month Int;

SELECT MonthlySales
INTO #NewSales
FROM ReportingMonthlySales RMS
JOIN CustomerCategoryLog CCL ON RMS.Unit = CCL.Unit
WHERE RMS.Unit = @Unit AND RMS.CYear = @Year AND RMS.Cmonth = @Month AND CCL.Subcategory = 'New'

SELECT MonthlySales
INTO #AssumedSales
FROM ReportingMonthlySales RMS
JOIN CustomerCategoryLog CCL ON RMS.Unit = CCL.Unit
WHERE RMS.Unit = @Unit AND RMS.CYear = @Year AND RMS.Cmonth = @Month AND CCL.Subcategory = 'Assumed'

SELECT MonthlySales
INTO #ExistingSales
FROM ReportingMonthlySales RMS
JOIN CustomerCategoryLog CCL ON RMS.Unit = CCL.Unit
WHERE RMS.Unit = @Unit AND RMS.CYear = @Year AND RMS.Cmonth = @Month AND CCL.Subcategory = 'Existing'

SELECT MonthlySales
INTO #OrganicSales
FROM ReportingMonthlySales RMS
JOIN CustomerCategoryLog CCL ON RMS.Unit = CCL.Unit
WHERE RMS.Unit = @Unit AND RMS.CYear = @Year AND RMS.Cmonth = @Month AND CCL.Subcategory = 'Organic'

将这四个临时表合二为一,将 MonthlySales 放入适当的 Category.Subcategory 列,并将其作为结果集返回以用于生成报告。

【问题讨论】:

  • 我将尽快(明天)奖励这个; “引起注意”或“奖励现有答案”取决于从现在到那时的活动。
  • 你的问题是什么?在编写计算所需内容的 SQL 查询时是否需要帮助?在这种情况下,请向我们提供样本数据和预期结果。或者您是否需要一种方法来“扩展”现有的遗留存储过程而不触及/更改它?在这种情况下解释为什么你不能改变它。
  • 是的,我需要查询方面的帮助。正如我在更新中所指出的那样,在 Bretana 的回答中证明了下面的那个可能有效,但它需要“永远”才能运行。

标签: sql-server tsql stored-procedures calculated-columns


【解决方案1】:

我认为这应该可行...您有太多不必要的连接。

Select cy.*, 
   n.MonthlySales newnew,
   a.MonthlySales NewAssumed,
   e.MonthlySales ExistingExisting,
   o.MonthlySales ExistingOrganic
from #CombinedYears CY 
   left join CustomerCategoryLog ccl
     on ccl.Unit = cy.Unit
   join ReportingMonthlySales n
     on n.Category = 'New' 
        and n.Subcategory = 'New' 
        and n.Unit = cy.Unit
   left join ReportingMonthlySales a
      on a.Category = 'New' 
        and a.Subcategory = 'Assumed' 
        and a.Unit = CY.Unit
   left join ReportingMonthlySales e
      on e.Category = 'Existing' 
        and e.Subcategory = 'Existing' 
        and e.Unit = CY.Unit
   left join ReportingMonthlySales o
      on o.Category = 'Existing' 
        and o.Subcategory = 'Organic' 
        and o.Unit = CY.Unit

【讨论】:

    【解决方案2】:

    这可能会对您有所帮助。代码未经测试(因为我没有你的数据库;)),但我希望它能让事情变得不那么混乱。

    最后;您正在尝试加入 CustomerCategoryLog 表,但您没有引用查询中的任何其他表。因此,您无法完成连接,这很可能是您无法编译的原因。

    DECLARE @CYear INT = 2016;
    DECLARE @Cmonth INT = 4;
    
    WITH myDerivedData (unit, cyear, cmonth, NumUnits, MonthlySales, YTDBudgetPerc, YTDSales) AS (
        SELECT rms.unit
              ,rms.cyear
              ,rms.cmonth
              ,COALESCE(SUM(rms.NumUnits), 0) AS NumUnits
              ,COALESCE(SUM(rms.MonthlySales), 0) AS MonthlySales
              ,CASE WHEN mups2.ProjectedSales = 0 THEN 1
                    ELSE mups2.ProjectedSales / 12 * @Cmonth
                    END AS YTDBudgetPerc
              ,COALESCE(SUM(rms2.MonthlySales), 0) AS YTDSales
          FROM ReportingMonthlySales AS rms
          LEFT OUTER JOIN ReportingMonthlySales AS rms2
                  ON rms2.unit = rms.unit
                 AND rms2.cyear = rms.cyear
                 AND rms2.cmonth = rms.cmonth
                 AND rms2.cmonth <= @Cmonth
          LEFT OUTER JOIN MasterUnitsProjSales AS mups
                  ON mups.unit = rms.unit
                 AND mups.cyear = rms.cyear
          LEFT OUTER JOIN MasterUnitsProjSales AS mups2
                  ON mups2.unit = rms.unit
                 AND mups2.cyear = rms.cyear
         WHERE rms.cyear = @Cmonth
           AND rms.cyear = @Cyear
         GROUP BY rms.Unit, rms.cyear, rms.cmonth
    )
    SELECT mups.CSDirector,
          ,mups.[Category]
          ,mups.[Segment]
          ,mups.unit
          ,mdd.NumUnits
          ,mdd.MonthlySales
          ,mdd.YTDSales
          ,SUM(mups.ProjectedSales) AS ProjSales
          ,SUM(mups.ProjectedSales) / 12 * @Cmonth AS YTDProjSales
          ,mdd.YTDBudgetPerc
      INTO #CombinedYears2
      FROM MasterUnitsProjSales AS mups
     INNER JOIN myDerivedData AS mdd
             ON mdd.unit = mups.unit
            AND mdd.cyear = mups.cyear
            AND mdd.cmonth = mups.cmonth
     WHERE mups.Cyear = @CYear
     GROUP BY mups.Unit, mups.CSDirector, mups.[Category], mups.[Segment], mdd.NumUnits, mdd.MonthlySales, mdd.YTDSales, mdd.YTDBudgetPerc
     ORDER BY mups.NewBiz, mups.Unit;
    
    SELECT cy.*, 
       rms.MonthlySales newnew,
       rms.MonthlySales NewAssumed,
       rms.MonthlySales ExistingExisting,
       rms.MonthlySales ExistingOrganic
      FROM #CombinedYears2 AS CY 
      LEFT OUTER JOIN ReportingMonthlySales AS rms
              ON rms.Unit = cy.Unit
      INNER JOIN CustomerCategoryLog AS n  /* What is this? You can't join a table like this, as it's not joined with any other table */
              ON n.Category = 'New';
    

    【讨论】:

    • 谢谢,帕特里克;在消除了几个多余的逗号之后(在一行的末尾和下一行的开头),并按照 LINQPad 的提示向 GROUPBY 子句添加了几个字段,我遇到了一个错误,我不知道怎么做解决:错误 2017:无效的列名 'cmonth' 无法绑定多部分标识符“mups2.ProjectedSales”。这是错误图标“装饰”的行:AND mdd.cmonth = mups.cmonth(从底部向上大约 15 行)
    • 喂!抱歉,GROUP BY 中的逗号和缺失的行。我已经更新了代码并试图纠正它。您得到的错误(可能)是因为我在复制/粘贴为 CTE 中的 MasterUnitsProjSales 创建第二个JOIN 时马虎。我也对此进行了更正。 LEFT OUTER JOIN MasterUnitsProjSales AS mups2 ON mups2.unit = rms.unit AND mups2.cyear = rms.cyear
    • 我仍然在“AND mdd.cmonth = mups.cmonth”上收到“错误 207: Invalid column name 'cmonth'”,这实际上是一个有效的投诉,因为 mups 表没有 CMonth 列(不过,它确实有一个 CYear)。我评论了那条线,但我不知道这是否会使查询的完整性无效。不再报错,其实LINQPad的底部说,“查询成功(00:00.015),但是整个SQL面板几乎是白的,文字是浅灰色的,而且没有结果……但是,无论如何。“成功”的消息让我觉得它已经完成了,很难。没有结果?但它没有这么说。
    • 在查询窗格中单击会使它再次看起来“正常”,但下面的窗格中仍然没有结果……真是痛苦。
    • 分别运行不同的部分,看看你在哪里得到结果,哪里没有。因为我写了一个INNER JOINmyDerivedData,很可能那部分没有返回任何数据,因此没有什么可以加入,这反过来又会产生一个空的结果集。
    【解决方案3】:

    如果您的原始存储过程还没有执行INSERT ... EXEC 语句,一个非常简单的方法如下:

    原始存储过程:

    create procedure myproc as
    begin
        select 1 as id union all select 2;
    end
    GO
    

    “扩展”存储过程:

    create proc myproc2 as
    begin
    
        create table #temp (id int);
        -- Get the results of the original s.p.
        insert #temp exec myproc;
    
        -- Add more columns:
        alter table #temp add double_id int;
    
        -- Populate the new columns
        update #temp set double_id = 2*id;
    
        -- Return the extended data set
        select * from #temp;
    end
    GO
    

    【讨论】:

    • 这个答案是基于您不打算修改原始存储过程的假设;如果您可以访问原始代码并愿意修改它(我希望不会破坏任何东西),您可以完全绕过这个答案。
    • 非常有趣;我可能最终会使用这样的东西,但我不确定如何将 vals 放入动态添加的列中。
    • ISTM 如果我走这条路,我将不得不遍历#temp 表。我不知道该怎么做。是否涉及游标?诅咒者!
    • 我不知道是否需要涉及游标(通常我们可以避免它们),因为我试图从字面上回答问题的标题......我担心如果你的问题是关于实际的您需要获取的数据及其组合我帮不上什么忙,除非没有样本数据和预期的输出。
    【解决方案4】:

    在 Sql Server 2012 中,

    CREATE procedure myproc @orderid int
    as
    begin
        select 1 as id union all select @orderid;
    end
    GO
    
    EXEC .myproc @orderid = 43671
    WITH RESULT SETS
    (
      (
        id        INT      NOT NULL
    
      )
    );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-15
      • 1970-01-01
      • 2016-04-04
      • 2011-07-24
      • 1970-01-01
      相关资源
      最近更新 更多