我正在一个非常受限的 T-SQL 环境中工作,在该环境中,人们只能定义
VIEW
的“主体”:大概是 ...
中的
CREATE VIEW My_View AS ...
@@VERSION
:
Microsoft SQL Server 2019 (RTM-CU19) (KB5023049) - 15.0.4298.1 (X64)
2023 年 1 月 27 日 16:44:09
版权所有 (C) 2019 微软公司
Linux (Amazon Linux 2) 上的网络版(64 位)
我应该注意,这些表格是从“平面文件”同步的,因此,原始来源中没有保留“正式”原理图结构。也就是说,所有“功能依赖关系”都只是从列名称和业务概念推断(尽管可靠)。 问题 假设我有下表,名为
My_Measures
Name
测量 | ||
---|---|---|
0 | 1 | |
10 | 2 | |
20 | 2 | |
30 |
Person_ID
。
平常现在假设我希望将Measure
为每个人的各种汇总统计数据。这在 SQL 中很简单...
SELECT
Person_ID,
MIN(Measure) AS Min_Measure,
MAX(Measure) AS Max_Measure,
AVG(Measure) AS Avg_Measure
FROM
My_Measures
GROUP BY
Person_ID
最大测量值 | 平均_测量 | ||
---|---|---|---|
10 | 5 | 2 | |
30 | 25 |
Name
Person_ID
,如下所示:
最小_测量 | 最大测量值 | 平均_测量 | ||
---|---|---|---|---|
0 | 10 | 5 | 2 | |
20 | 30 | 25 |
SELECT
Person_ID,
-- ⌄⌄⌄⌄⌄
Name,
-- ^^^^^
MIN(Measure) AS Min_Measure,
MAX(Measure) AS Max_Measure,
AVG(Measure) AS Avg_Measure
FROM
My_Measures
GROUP BY
Person_ID
...将因以下错误而失败:
列“My_Measures.Name”在选择列表中无效,因为它未包含在聚合函数或 GROUP BY 子句中。
尝试un我找到了几种
(1) GROUP BY
因变量
GROUP BY
Name
列
after
Person_ID
;更一般地说,将因变量附加在 GROUP BY
子句的 end处:
SELECT
Person_ID,
-- ⌄⌄⌄⌄⌄
Name,
-- ^^^^^
MIN(Measure) AS Min_Measure,
MAX(Measure) AS Max_Measure,
AVG(Measure) AS Avg_Measure
FROM
My_Measures
GROUP BY
-- ⌄⌄⌄⌄⌄⌄
Person_ID, Name
-- ^^^^^^
这使得分组保持不变,因为“真实”分组变量(此处Person_ID
)已经定义了它,而因变量只是“标记”。然而,这会浪费处理(任意多个)因变量的性能,出于索引目的,这些因变量可能会更复杂(
CHAR
字符串表示
Name
)。(2)“聚合”因变量另一种方法是“聚合”Name
MIN()
)为我们提供来自许多相同重复项(如
'Greg'
)的一个代表值(如('Greg', 'Greg')
)。SELECT
Person_ID,
-- ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
MIN(Name) AS Name,
-- ^^^^^^^^^^^^^^^^^^^^^
MIN(Measure) AS Min_Measure,
MAX(Measure) AS Max_Measure,
AVG(Measure) AS Avg_Measure
FROM
My_Measures
GROUP BY
Person_ID
这同样达到了预期的结果,但同样浪费了计算许多相同值的聚合的性能。此外,它只适用于 comparable并因此具有
MIN()
的值;但对于非可比较的数据类型来说,它显然会失败。
(3) 聚合后重新JOIN
也许最令人失望的方法是简单地计算聚合,然后通过 Person_ID
:将
Name
与其
JOIN
重新关联
-- Aggregate by ID.
WITH agg AS(
SELECT
Person_ID,
MIN(Measure) AS Min_Measure,
MAX(Measure) AS Max_Measure,
AVG(Measure) AS Avg_Measure
FROM
My_Measures
GROUP BY
Person_ID
-- Deduplicate names for the JOIN. Given functional dependency, DISTINCT suffices.
), msr AS (
SELECT DISTINCT
Person_ID,
Name
FROM My_Measures
-- Reassociate the names with their IDs.
) SELECT
agg.Person_ID AS Person_ID,
-- ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
msr.Name AS Name,
-- ^^^^^^^^^^^^^^^^^^^^^^^^
agg.Min_Measure AS Min_Measure,
agg.Max_Measure AS Max_Measure,
agg.Avg_Measure AS Avg_Measure
FROM
-- ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
agg INNER JOIN msr
-- ^^^^^^^^^^^^^^
ON agg.Person_ID = msr.Person_ID
显然,这在不必要的
JOIN
和多个CTE上浪费了大量资源,所有这些都是为了恢复我们最初
拥有
的数据(如
Name
)!
(4) 将
FIRST_VALUE()
越过 PARTITION
我在 T-SQL 中搜索了 R 中
first()
FIRST()
只需从许多相同的重复值 (
) 中选择非常first 值 (
'Greg'
) ('Greg', 'Greg')
)在GROUP
内,不需要任何昂贵的计算。此外,无论可比性如何,这都会起作用。
我偶然发现了
FIRST_VALUE()
函数,但这似乎每次使用都需要一个 PARTITION
,而且——由于我对优化 PARTITION
相对缺乏经验——我担心如果
很多的话会对性能产生影响因变量必须是
SELECT
ed。
它看起来也很丑。 ́\(ツ)/́
问题将任意一组因变量(如
SELECT
)与分组变量(如Name
优雅
和规范性
,最后是可扩展性
:理想情况下,这应该适用于所有数据类型,甚至是非可比较的数据类型。
Person_ID