SQL Server ->> 间接实现COUNT(DISTINCT XXX) OVER(PARTITION BY YYY)

SQL Server 2005版本开始支持了窗口函数(Windowing Function)和OVER字句。SQL Server 2012版本开始支持了窗口函数的ORDER BY字句实现连续/累计聚合功能。但是有个功能到SQL Server 2014版本为止(从目前SQL Server 2016 CTP3来看,还是不支持),就是COUNT(DISTINCT XXX) OVER(PARTITION BY YYY)。

一直觉得这个事情没有办法用比较巧妙地办法做到,只能是用CROSS APPLY或者循环去把数据集“UNION ALL”起来。今天在Google上找到了一个别人想到的办法,顿时觉得自己怎么那么笨,没有想到这个办法呢。巧妙之处在于用一个带有DENSE_RANK函数的子查询配合MAX函数实现。下面是测试代码:

 

CREATE TABLE Foo (
fookey INT PRIMARY KEY,
company VARCHAR(30),
sales_rep VARCHAR(30),
client VARCHAR(30));

INSERT INTO Foo VALUES(1, 'ABC Corp.', 'Joe', 'Client1');
INSERT INTO Foo VALUES(2, 'ABC Corp.', 'Joe', 'Client2');
INSERT INTO Foo VALUES(6, 'ABC Corp.', 'Joe', 'Client2');
INSERT INTO Foo VALUES(3, 'ABC Corp.', 'Peter', 'Client2');
INSERT INTO Foo VALUES(4, 'DEF Corp.', 'Joe', 'Client1');
INSERT INTO Foo VALUES(5, 'DEF Corp.', 'Joe', 'Client3');

SELECT DISTINCT company, sales_rep, client,
MAX(rk1) OVER(PARTITION BY sales_rep) AS rep_distinct_client_cnt,
MAX(rk2) OVER(PARTITION BY company) AS company_distinct_client_cnt
FROM (
SELECT fookey, company, sales_rep, client,
DENSE_RANK() OVER(PARTITION BY sales_rep ORDER BY client) As rk1,
DENSE_RANK() OVER(PARTITION BY company ORDER BY client) As rk2
FROM Foo) AS F;

 

这个办法精妙的地方便是利用了DENSE_RANK本身会对相同值返回相同的排序号的特点,这点恰恰符合了我们需要DISTINCT的作用。其次,排序号和COUNT的相同之处不就是对记录的个数统计吗?那么取得最大的排序号不就相当于拿到了COUNT的值了吗?确实高明。感叹自己过去怎么就没想到呢?还是很开心可以知道这个办法的。

转载于:https://www.cnblogs.com/jenrrychen/p/5131410.html

<think>好的,我现在需要帮助用户确认MySQLSQL查询的执行顺序是否正确。用户提供的顺序是FROM → ON → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT,并引用了相关资源。首先,我需要回忆MySQL官方文档中的执行顺序,并结合用户的引用内容来验证。 首先,用户提到的引用[2]和[3]中提到了SQL查询关键字的执行顺序,顺序大致相同,但需要仔细核对每个步骤的位置是否正确。例如,JOIN和ON通常在FROM之后处理,然后是WHERE子句。接下来是GROUP BY和HAVING,之后才是SELECT和DISTINCT。然后是ORDER BY和LIMIT。 我需要确认是否正确处理了各个阶段,特别是ON条件和JOIN的顺序,以及DISTINCT是在SELECT之后还是在其他位置。根据我的知识,执行顺序应该是从FROM开始,确定数据源,然后处理JOIN和ON条件,接着应用WHERE过滤,再进行GROUP BY分组,之后是HAVING对分组后的结果过滤,然后处理SELECT选择字段,应用DISTINCT去重,之后排序ORDER BY,最后用LIMIT限制结果数量。 同时,需要考虑到子查询、聚合函数的位置,以及别名是否在SELECT阶段之后才能被后续步骤使用。例如,HAVING可以使用聚合函数,而WHERE不能,因为WHERE在GROUP BY之前执行。同时,ORDER BY可以使用SELECT中定义的别名,因为它是在SELECT之后执行的。 用户提供的顺序是否正确呢?根据引用[3],SQL语句的执行顺序确实是FROM → ON → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT。这与我之前的理解一致。但需要确保没有遗漏步骤,比如UNION或子查询的情况,但用户的问题中没有提到这些,可能不需要涉及。 另外,需要注意的是,虽然执行顺序如此,但数据库优化器可能会调整某些步骤的顺序以提高性能,只要不影响最终结果。但逻辑上的执行顺序是用户提到的顺序。 因此,结论是用户的顺序是正确的,但需要补充一些细节,比如ON和JOIN的具体处理,以及各个阶段的作用,比如WHERE在GROUP BY之前,HAVING在之后,DISTINCT在SELECT之后应用。同时,需要引用用户提供的参考资料,特别是引用[2]和[3]来支持结论。</think>MySQLSQL查询的**逻辑执行顺序**确实遵循以下步骤[^2][^3]: 1. **FROM** 确定主表和连接的数据源,包括子查询生成的临时表 2. **ON** 处理连接条件(如`JOIN ... ON`中的筛选条件) 3. **JOIN** 根据连接类型合并数据(如`LEFT JOIN`会保留左表所有记录) 4. **WHERE** 对合并后的数据进行行级过滤 *注意:WHERE阶段不能使用SELECT中定义的别名或聚合函数* 5. **GROUP BY** 对数据进行分组,通常与聚合函数配合使用 6. **HAVING** 对分组后的数据进行过滤 *与WHERE的区别:可包含聚合函数如`HAVING COUNT(*) > 1`* 7. **SELECT** 选择最终输出的列,计算表达式,生成结果集 *此阶段才会处理列别名,如`SELECT total AS t`* 8. **DISTINCT** 对SELECT的结果集去重 9. **ORDER BY** 对结果进行排序 *可使用SELECT阶段定义的别名,如`ORDER BY t DESC`* 10. **LIMIT** 限制返回结果的行数 --- ### 关键验证示例 ```sql SELECT age AS a, COUNT(*) cnt -- SELECT阶段定义别名a和cnt FROM users u JOIN orders o ON u.user_id = o.user_id -- ON在JOIN前执行 WHERE a > 30 -- 错误!WHERE阶段无法使用别名a GROUP BY age HAVING cnt > 5 -- 正确!HAVING可使用聚合函数 ORDER BY cnt DESC -- 正确!ORDER BY可使用别名cnt LIMIT 10; ``` 此查询会报错,因为`WHERE a > 30`试图使用SELECT阶段定义的别名`a`,而WHERE的实际执行顺序在SELECT之前。 --- ### 需要特别注意的特性 1. **窗口函数**(如`ROW_NUMBER()`)在SELECT之后执行 2. **UNION**操作符在所有子查询执行完毕后才会处理 3. **优化器调整**:物理执行顺序可能与逻辑顺序不同,但保证结果等价
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值