好的,我从this问题知道为什么我无法引用WHERE
,GROUP BY
或HAVING
语句中的别名列。
我的问题是我将这个查询从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 By
或Having
语句中的别名列问题。
有没有办法解决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
答案 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;
两个查询的执行计划完全相同:
所以你的查询最终会像:
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;