在SQL Server 2012中使用别名列

时间:2015-04-01 16:33:38

标签: sql sql-server alias

好的,我从this问题知道为什么我无法引用WHEREGROUP BYHAVING语句中的别名列。

我的问题是我将这个查询从Teradata数据库移到SQL Server 2012.

在Teradata中,在where,group by,having,甚至join语句中引用别名列是完全有效的。

我的问题是,如何在SQL Server中执行此查询以及其他类似查询,而不必先求助于填充临时表。 (此示例只是包含10个单独事务的大型tSQL脚本的一部分,其中许多事务比提供的示例复杂或更复杂)

SELECT 
    MAX(CASE WHEN 
            Field_Name = 'Parent Brand Cd' AND 
            DATALENGTH(Field_Val)>1 
        THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) 
        ELSE NULL 
        END
        ) AS Parent_Brand_Cd,
    MAX(CASE WHEN 
            Field_Name = 'Brand Id' AND 
            DATALENGTH(Field_Val)>1 
        THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) 
        ELSE NULL 
        END
        ) AS Hotel_Cd,
    @src_sys_id AS Src_Sys_Id,
    MAX(CASE WHEN 
            Field_Name = 'Brand' AND 
            DATALENGTH(Field_Val)>1 
        THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) 
        ELSE NULL 
        END
        ) AS Temp1,
    CASE Temp1 
        WHEN 'Company1' 
            THEN 'c1'
        WHEN 'Company2' 
            THEN 'c2' 
        WHEN 'Company3' 
            THEN 'c3' 
        ELSE TEmp1 
    END AS Brand_Cd,
    @process_id AS Insert_Process_Id
 FROM dbo.Company -- STAGING
 GROUP BY Parent_Brand_Cd 
 HAVING 
    Parent_Brand_Cd IS NOT NULL AND 
    Hotel_Cd IS NOT NULL

此查询的第一个问题是它正在创建别名列Temp1,然后立即尝试对其执行CASE语句。我可以通过这样做来纠正这个:

MAX(CASE WHEN 
            Field_Name = 'Brand' AND 
            DATALENGTH(Field_Val)>1 
        THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) 
        ELSE NULL 
        END
        ) AS Temp1,
    MAX(CASE WHEN 
            Field_Name = 'Brand' AND 
            DATALENGTH(Field_Val)>1 
        THEN 
            CASE 
                WHEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) = 'Company1' THEN 'c1'
                WHEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) = 'Company2' THEN 'c2'
                WHEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) = 'Company' THEN 'c3'
                ELSE SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1)
            END
        ELSE NULL 
        END
        ) AS Brand_Cd,

但是这对于脚本的其他部分没有帮助,其中别名列是计算的,然后在其他计算中使用。此外,它无法解决Group ByHaving语句中的别名列问题。

有没有办法解决SQL Server中别名列的限制,而无需在整个地方创建和填充临时表?

编辑:GarethD提议的工作解决方案

SELECT  
            Parent_Brand_Cd,
            Hotel_Cd,
            @src_sys_id AS Src_Sys_Id,
            CASE Temp1 
                WHEN 'Company1' THEN 'C1'
                WHEN 'Company2' THEN 'C2' 
                WHEN 'Company3' THEN 'C3' 
                ELSE TEmp1 
            END AS Brand_Cd,
            @process_id AS Insert_Process_Id,
            @process_id AS Update_Process_Id
        FROM    
            (   
                SELECT  
                    MAX(CASE WHEN Field_Name = 'Parent Brand Cd' THEN FieldValue END) AS Parent_Brand_Cd,
                    MAX(CASE WHEN Field_Name = 'Brand Id' THEN FieldValue END) AS Hotel_Cd,
                    MAX(CASE WHEN Field_Name = 'Brand' THEN FieldValue END) AS Temp1
                FROM    
                    (
                        SELECT  
                            Field_Name,
                            FieldValue = CASE WHEN DATALENGTH(Field_Val) > 1 THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) END
                        FROM    DEV_STG_TB.dbo.Company_Attributes_3 -- STAGING
                    ) AS c

            ) AS sub
        WHERE   
            Parent_Brand_Cd IS NOT NULL AND 
            Hotel_Cd IS NOT NULL
        GROUP BY 
            Parent_Brand_Cd,
            Hotel_Cd,
            Temp1

1 个答案:

答案 0 :(得分:4)

您可以将查询移动到子查询中,并引用别名。 SQL Server非常聪明,能够以与having子句相同的方式优化它(在我至少完成的测试中)。请考虑以下两个查询:

SELECT  Name, [Count]
FROM    (   SELECT  name, [Count] = COUNT(*)
            FROM    sys.Columns
            GROUP BY name
        ) AS sub
WHERE   [Count] > 1;

SELECT  name, [Count] = COUNT(*)
FROM    sys.Columns
GROUP BY name
HAVING COUNT(*) > 1;

两个查询的执行计划完全相同:

enter image description here

所以你的查询最终会像:

SELECT  Parent_Brand_Cd,
        Hotel_Cd,
        @src_sys_id AS Src_Sys_Id,
        Temp1
        CASE Temp1 
            WHEN 'Company1' THEN 'c1'
            WHEN 'Company2' THEN 'c2' 
            WHEN 'Company3' THEN 'c3' 
            ELSE TEmp1 
        END AS Brand_Cd,
        @process_id AS Insert_Process_Id
FROM    (   SELECT  MAX(CASE WHEN Field_Name = 'Parent Brand Cd' AND DATALENGTH(Field_Val) > 1 THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) END) AS Parent_Brand_Cd,
                    MAX(CASE WHEN Field_Name = 'Brand Id' AND DATALENGTH(Field_Val) > 1 THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) END) AS Hotel_Cd,
                    MAX(CASE WHEN Field_Name = 'Brand' AND DATALENGTH(Field_Val) > 1 THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) END) AS Temp1
            FROM    dbo.Company -- STAGING
            GROUP BY Parent_Brand_Cd 
        ) AS sub
WHERE   Parent_Brand_Cd IS NOT NULL 
AND     Hotel_Cd IS NOT NULL;

您甚至可以通过使用另一个子查询来进一步减少重复的表达式:

SELECT  Parent_Brand_Cd,
        Hotel_Cd,
        @src_sys_id AS Src_Sys_Id,
        Temp1
        CASE Temp1 
            WHEN 'Company1' THEN 'c1'
            WHEN 'Company2' THEN 'c2' 
            WHEN 'Company3' THEN 'c3' 
            ELSE TEmp1 
        END AS Brand_Cd,
        @process_id AS Insert_Process_Id
FROM    (   SELECT  MAX(CASE WHEN Field_Name = 'Parent Brand Cd' THEN FieldValue END) AS Parent_Brand_Cd,
                    MAX(CASE WHEN Field_Name = 'Brand Id' THEN FieldValue END) AS Hotel_Cd,
                    MAX(CASE WHEN Field_Name = 'Brand' THEN FieldValue END) AS Temp1
            FROM    (   SELECT  Parent_Brand_Cd,
                                Field_Name,
                                FieldValue = CASE WHEN DATALENGTH(Field_Val) > 1 THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) END
                        FROM    dbo.Company -- STAGING
                    ) AS c
            GROUP BY Parent_Brand_Cd 
        ) AS sub
WHERE   Parent_Brand_Cd IS NOT NULL 
AND     Hotel_Cd IS NOT NULL;

注意,我已从案例表达式中删除ELSE NULL,因为这是多余的。

我非常喜欢使用Common Table Expressions代替子查询来消除我的查询(这完全是主观的),并且还使用PIVOT,所以我个人会将上面重写为:< / p>

WITH CompanyCTE AS
(   SELECT  Parent_Brand_Cd,
            Field_Name,
            FieldValue = CASE WHEN DATALENGTH(Field_Val) > 1 
                            THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) 
                        END
    FROM    dbo.Company 
)
SELECT  pvt.Parent_Brand_Cd
        Parent_Brand_Cd = pvt.[Parent Brand Cd],
        Hotel_Cd = pvt.[Brand Id],
        Temp1 = pvt.[Brand]
FROM    CompanyCTE AS c
        PIVOT
        (   MAX(FieldValue)
            FOR Field_Name IN ([Parent Brand Cd], [Brand Id], [Brand])
        ) AS pvt
WHERE   pvt.[Parent Brand Cd] IS NOT NULL
AND     pvt.[Brand Id] IS NOT NULL;

另一个优点是PIVOT可让您直接访问聚合列。

当然,您的另一个选择是重复聚合函数:

HAVING MAX(CASE WHEN 
                Field_Name = 'Parent Brand Cd' AND 
                DATALENGTH(Field_Val)>1 
            THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) 
            ELSE NULL 
            END
            ) IS NOT NULL
AND    MAX(CASE WHEN 
                Field_Name = 'Parent Brand Cd' AND 
                DATALENGTH(Field_Val)>1 
            THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) 
            ELSE NULL 
            END
            ) IS NOT NULL;

<强>附录

看到工作解决方案后,您根本不需要任何分组,所以我认为以下内容对您有用:

SELECT  Parent_Brand_Cd,
        Hotel_Cd,
        @src_sys_id AS Src_Sys_Id,
        Temp1
        CASE Temp1 
            WHEN 'Company1' THEN 'c1'
            WHEN 'Company2' THEN 'c2' 
            WHEN 'Company3' THEN 'c3' 
            ELSE TEmp1 
        END AS Brand_Cd,
        @process_id AS Insert_Process_Id
FROM    (   SELECT  MAX(CASE WHEN Field_Name = 'Parent Brand Cd' THEN FieldValue END) AS Parent_Brand_Cd,
                    MAX(CASE WHEN Field_Name = 'Brand Id' THEN FieldValue END) AS Hotel_Cd,
                    MAX(CASE WHEN Field_Name = 'Brand' THEN FieldValue END) AS Temp1
            FROM    (   SELECT  Field_Name,
                                FieldValue = CASE WHEN DATALENGTH(Field_Val) > 1 THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) END
                        FROM    dbo.Company -- STAGING
                    ) AS c
        ) AS sub
WHERE   Parent_Brand_Cd IS NOT NULL 
AND     Hotel_Cd IS NOT NULL;

因为parent_brand_cd定义是唯一的,因为它是从没有分组的聚合派生的,所以任何进一步的分组虽然无关紧要,但是是多余的。

或PIVOT解决方案。

WITH CompanyCTE AS
(   SELECT  Field_Name,
            FieldValue = CASE WHEN DATALENGTH(Field_Val) > 1 
                            THEN SUBSTRING(Field_Val, 1, DATALENGTH(Field_Val) - 1) 
                        END
    FROM    dbo.Company 
)
SELECT  Parent_Brand_Cd = pvt.[Parent Brand Cd],
        Hotel_Cd = pvt.[Brand Id],
        Src_Sys_Id = @src_sys_id,
        Temp1 = pvt.[Brand],
        Brand_Cd = CASE pvt.[Brand] 
                        WHEN 'Company1' THEN 'c1'
                        WHEN 'Company2' THEN 'c2' 
                        WHEN 'Company3' THEN 'c3' 
                        ELSE TEmp1 
                    END,
        Insert_Process_Id = @process_id 
FROM    CompanyCTE AS c
        PIVOT
        (   MAX(FieldValue)
            FOR Field_Name IN ([Parent Brand Cd], [Brand Id], [Brand])
        ) AS pvt
WHERE   pvt.[Parent Brand Cd] IS NOT NULL
AND     pvt.[Brand Id] IS NOT NULL;