【问题标题】:Create SQL Function/Procedure from 3 Tables从 3 个表创建 SQL 函数/过程
【发布时间】:2019-06-22 11:57:35
【问题描述】:

我有 3 个表格,类别、资源人力和人力资源计划。

我想按类别计算数量

我创建了这个 SQL 查询:

select 
    CostCategory.CostCategory, sum(isnull(HRPplan.Amount, 0))
from 
    CostCategory
left join 
    HRPplan on HRPplan.Month between 1 and 3 
            and HRPplan.Year = 2019 
            and HRPplan.IDRPH in (select HumanResource.IDRPH 
                                  from HumanResource
                                  where HumanResource.IDCostCategory = CostCategory.IDCostegory)
group by  
    CostCategory.CostCategory

它返回一个结果,但我还有另外 2 个表与 Human Resource & HPlan 完全一样,用它来计算数量,所以如果我使用上面提到的相同代码,查询性能不好。而且我不会如何在 SQL 中使用函数/过程,因为年份和月份将作为参数插入。

【问题讨论】:

    标签: sql tsql stored-procedures sql-server-2016


    【解决方案1】:

    在问题中获取 DDL 和示例数据 DML 可以更轻松地提供快速解决方案。此外,您的查询中使用的列与您提供的屏幕截图不完全相似,因此我根据提供的屏幕截图提供这些方法。

    准备好DDL和测试数据:

    DROP TABLE IF EXISTS CostCategory;
    GO
    
    CREATE TABLE CostCategory
    (
        IDCostCategory  INT
        ,CostCategory   VARCHAR(10)
    ) ON [PRIMARY]
    GO
    INSERT INTO CostCategory
    VALUES
    (1, 'A'), (2, 'B'), (3, 'C')
    GO
    
    
    DROP TABLE IF EXISTS HumanResource;
    GO
    
    CREATE TABLE HumanResource
    (
        IDHR            INT
        ,IDCostCategory INT
    ) ON [PRIMARY]
    GO
    INSERT INTO HumanResource
    VALUES
    (1, 1), (2, 2), (3, 2)
    GO
    
    
    
    DROP TABLE IF EXISTS HRPlan;
    GO
    
    CREATE TABLE HRPlan
    (
        IDHRPlan        INT
        ,IDHR           INT
        ,Amount         INT
        ,Month          INT
        ,Year           INT
    ) ON [PRIMARY]
    GO
    INSERT INTO HRPlan
    VALUES
        (1, 1, 10 , 1, 2019)
    ,   (2, 1, 12 , 2, 2019)
    ,   (3, 1, 15 , 2, 2019)
    ,   (4, 2, 50 , 1, 2019)
    ,   (5, 2, 0 , 2, 2019)
    ,   (6, 2, 100 , 3, 2019)
    ,   (4, 3, 40 , 1, 2019)
    ,   (5, 3, 50 , 2, 2019)
    ,   (6, 3, 30 , 3, 2019)
    GO
    

    为了跟踪性能,您可以将 IO 和时间统计设置为 ON:

    SET STATISTICS IO ON;
    SET STATISTICS TIME ON;
    

    解决方案 1:使用外部应用(比下面的解决方案 2 性能更好)

    SELECT  CC.CostCategory, ISNULL(OA.Amount, 0)
    FROM    CostCategory CC
            OUTER APPLY (
                            SELECT  Amount = SUM(ISNULL(HP.Amount, 0))
                            FROM    HRPlan HP
                            WHERE   HP.Month BETWEEN 1 AND 3
                            AND     HP.Year = 2019  
                            AND     EXISTS  (
                                                SELECT  1
                                                FROM    HumanResource HR
                                                WHERE   HR.IDCostCategory = CC.IDCostCategory
                                                AND     HR.IDHR = HP.IDHR
                                            )
                        ) OA;
    

    解决方案 2: 使用左连接

    SELECT  CC.CostCategory, ISNULL(SUM(HP.Amount), 0)
    FROM    CostCategory CC
            LEFT JOIN HumanResource HR
                ON  CC.IDCostCategory = HR.IDCostCategory
            LEFT JOIN HRPlan HP
                ON HR.IDHR = HP.IDHR
                AND HP.Month BETWEEN 1 AND 3
                AND HP.Year = 2019
    GROUP   BY CC.CostCategory;
    

    注意:- 您可以考虑在 HRPlan 表上创建列存储索引,并在其他 2 个覆盖连接和选定列的表上创建行存储/列存储索引以获得更好的性能。

    【讨论】:

    • 非常感谢,第一个解决方案是最好的解决方案,我计算了 4 列的数量。
    • 只是一个信息,在解决方案1中,您需要在“OA”之后添加“GROUP BY CC.CostCategory”
    • @MedAmin,不,你没有。如果在此处添加 GROUP BY,则查询将引发错误,因为外部查询没有进行聚合。正在查询的 OUTER APPLY 部分进行聚合。如果 CostCategory 表中有重复项,则必须在查询中使用 DISTINCT。
    • 列 'cost_category.CostCategory' 在选择列表中无效,因为它既不包含在聚合函数中,也不包含在 GROUP BY 子句中。所以这是错误,我需要使用 GROUP BY 来显示结果。
    • @MedAmin 2> 上述解决方案是针对给定问题的。如果您有更多列,或者您尝试编写的实际查询不同,或者您尝试进行一些额外的聚合,则必须相应地更改查询。如果您的要求与此处发布的不同,请在 StackOverflow 中发布一个单独的问题。 3>一旦你为表名使用了别名,你需要使用别名来引用你选择中的列(如果这是你的问题)
    【解决方案2】:

    为了提高性能,您可以避免使用 in 子句

    select CostCategory.CostCategory, sum(isnull(HRPplan.Amount,0))
    from CostCategory
    left join HRPplan on HRPplan.Month between 1 and 3  and HRPplan.Year = 2019 
    LEFT JOIN HumanResource ON HumanResource.IDCostCategory = CostCategory.IDCostegory
      and umanResource.IDRPH  = HRPplan.IDRPH 
    group by CostCategory.CostCategory 
    

    【讨论】:

    • 它只会显示带有值的记录,我想显示所有记录,即使数量为 0
    • 我不认为这个查询会给出被问到的内容。加入顺序需要更改。您可以看看我在下面提供的 2 个解决方案并告诉我。
    猜你喜欢
    • 1970-01-01
    • 2016-08-18
    • 1970-01-01
    • 2016-09-24
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 2017-01-20
    • 2020-06-04
    相关资源
    最近更新 更多