在 SQL Server 2012 中使用别名列
Working with Aliased Columns in SQL Server 2012
好的,所以我从 this 问题中知道为什么我不能从 WHERE
、GROUP BY
或 HAVING
语句中引用别名列。
我的问题是我有这个查询正在从 Teradata 数据库移动到 SQL Server 2012。
在 Teradata 中,在 where、group by、having 甚至 join 语句中引用别名列是完全有效的。
我的问题是,我如何才能在 SQL 服务器中执行此查询以及其他类似的查询,而不必首先从 table 填充临时 select . (这个例子只是包含 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 By
或 Having
语句中别名列的问题。
有什么方法可以绕过 SQL 服务器中别名列的限制,而不必到处创建和填充临时 table?
编辑: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
您可以将查询移动到子查询中,并引用您的别名。 SQL 服务器足够聪明,能够以与 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;
两个查询的执行计划完全一样:
所以你的查询最终会像这样:
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;
注意,我从 case 表达式中删除了 ELSE NULL
,因为这是多余的。
我非常喜欢使用Common Table Expressions instead of subqueries to de-clutter my queries (this is entirely subjective), and also using PIVOT
,所以我个人将上面的代码重写为:
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;
好的,所以我从 this 问题中知道为什么我不能从 WHERE
、GROUP BY
或 HAVING
语句中引用别名列。
我的问题是我有这个查询正在从 Teradata 数据库移动到 SQL Server 2012。
在 Teradata 中,在 where、group by、having 甚至 join 语句中引用别名列是完全有效的。
我的问题是,我如何才能在 SQL 服务器中执行此查询以及其他类似的查询,而不必首先从 table 填充临时 select . (这个例子只是包含 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 By
或 Having
语句中别名列的问题。
有什么方法可以绕过 SQL 服务器中别名列的限制,而不必到处创建和填充临时 table?
编辑: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
您可以将查询移动到子查询中,并引用您的别名。 SQL 服务器足够聪明,能够以与 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;
两个查询的执行计划完全一样:
所以你的查询最终会像这样:
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;
注意,我从 case 表达式中删除了 ELSE NULL
,因为这是多余的。
我非常喜欢使用Common Table Expressions instead of subqueries to de-clutter my queries (this is entirely subjective), and also using PIVOT
,所以我个人将上面的代码重写为:
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;