比如说今天是2023/3/27,sql怎么得出的结果是2023/1/27 2023/2/27 2023/3/27这几个呢,也就是同年(2023年),月份小于等于3月,日等于27,如果是月份最后一天的话,如2023/3/31则得出的结果是2023/1/31 2023/2/28 2023/3/31也就是各个月的最后一天,请问SQL怎么写呢
SELECT
CASE
WHEN MONTH(LAST_DAY('2023/3/27')) < 3 THEN DATE_FORMAT('2023/3/27', '%Y/1/%d')
WHEN MONTH(LAST_DAY('2023/3/27')) = 3 THEN DATE_FORMAT('2023/3/27', CONCAT('%Y/2/', IF(DAY(LAST_DAY('2023/3/27')) = 31, '28', '27')))
ELSE DATE_FORMAT('2023/3/27', '%Y/%m/27')
END AS 'Result'
3.4.1 分组查询介绍
数据分组的目的是用来汇总数据或为整个分组显示单行的汇总信息,通常在查询结果集中使用GROUP BY子句对记录进行分组。
在SELECT语句中,GROUP BY子句位于FROM子句之后,其语法格式如下:
SELECT columns_list
FROM table_name
[WHERE conditional_expression]
GROUP BY columns_list
**3.4.2 使用GROUP BY进行单列分组 **
单列分组是指基于列生成分组统计结果,当进行单列分组时,会基于分组列的每个不同值生成一个统计结果。
select deptno, job from emp group by deptno, job order by deptno;
GROUP BY子句经常与聚集函数一起使用。使用GROUP BY子句和聚集函数,可以实现对查询结果中每一组数据进行分类统计。所以,在结果中每组数据都有一个与之对应的统计值。在Oracle系统中,经常使用的统计函数如图所示。
select job, sum(sal) from emp group by job;
查询结果
JOB SUM(SAL)
------------------ ----------
CLERK 4150
SALESMAN 5600
PRESIDENT 5000
MANAGER 8275
ANALYST 6000
在使用GROUP BY子句时,要注意:
GROUP BY子句中的列可以不在SELECT列表中。
select sum(sal) from emp group by job;
查询结果
SUM(SAL)
----------
4150
5600
5000
8275
6000
3.4.3 使用GROUP BY进行多列分组
多列分组是指基于两个或两个以上的列生成分组统计结果。当进行多列分组时,会基于多个列的不同值生成统计结果。
select deptno, job, avg(sal), max(sal) from emp group by deptno, job;
3.4.4 使用ORDER BY改变分组排序结果
当使用GROUP BY子句执行分组统计时,会自动基于分组列进行升序排列。为了改变分组数据的排序结果,需要使用ORDER BY子句。
select deptno,sum(sal) from emp group by deptno order by sum(sal) desc;
3.4.5 使用HAVING子句限制分组结果
HAVING子句通常与GROUP BY子句一起使用,在完成对分组结果统计后,可以使用HAVING子句对分组的结果做进一步的筛选。
如果不使用GROUP BY子句,HAVING子句的功能与WHERE子句一样。HAVING子句和WHERE子句的相似之处都是定义搜索条件,唯一不同的是HAVING子句中可以包含聚合函数,如常用的(count)、(avg)、(sum)等,在WHERE子句中则不可以使用聚合函数。
select deptno as 部门编号,
avg(sal) as 平均工资
from emp
group by deptno
having avg(sal) > 2000 ;
上面的示例无法使用WHERE子句直接过滤出平均工资大于2000的部门信息,因为在WHERE子句中不可以使用聚合函数(这里是AVG)。
通常情况下,HAVING子句与GROUP BY子句一起使用,这样可以汇总相关数据后再进一步筛选汇总的数据。
3.4.6 在GROUP BY中使用ROLLUP和CUBE操作符
默认情况下,当使用GROUP BY子句生成数据统计结果时,只会生成相关列的数据统计信息,而不会生成小计和总计统计。
在实际应用程序中,不仅需要获得以上统计结果,而且可能还需要取得横向、纵向小计统计以及总计统计,例如部门的平均工资、岗位的平均工资、所有雇员的平均工资等。为了取得更全面的数据统计,可以使用ROLLUP和CUBE操作符。
(1)使用ROLLUP操作符执行数据统计
当使用ROLLUP操作符时,在保留原有统计结果的基础上,还会生成横向小计和总计。
select deptno as 部门编号,
job as 岗位,
avg(sal) as 平均工资
from emp
group by rollup(deptno,job) ;
(2)使用CUBE操作符执行数据统计
当使用CUBE操作符时,在保留原有统计结果的基础上,还会生成横向小计、纵向小计岗位平均工资和总计。
select deptno as 部门编号,
job as 岗位,
avg(sal) as 平均工资
from emp
group by cube(deptno,job) ;
(3)使用GROUPING函数
当使用ROLLUP或者CUBE操作符生成统计结果时,某个统计结果行可能用到一列或者多列,也可能没有使用任何列。为了确定统计结果是否使用了特定列,可以使用GROUPING函数。如果该函数返回0,则表示统计结果使用了该列;如果函数返回1,则表示统计结果没有使用该列。
select deptno,job, sum (sal),grouping(deptno),grouping(job)
from emp
group by rollup(deptno,job) ;
(4)在ROLLUP操作符中使用复合列
复合列被看作一个逻辑单元的列组合,当引用复合列时,需要用括号括住相关列。通过在ROLLUP操作符中使用复合列,可以略过ROLLUP操作符的某些统计结果。
例如,子句GROUP BY ROLLUP(a,b,c)的统计结果等同于GROUP BY(a,b,c)、GROUP BY(a,b)、GROUP BY a以及GROUP BY ( )的并集;而如果将(b,c)作为复合列,那么子句GROUP BY ROLLUP(a,(b,c))的结果等同于GROUP BY(a,b,c)、GROUP BY a以及GROUP BY ()的并集。
select deptno,job, sum (sal)
from emp
group by rollup((deptno,job)) ;
6.使用GROUPING SETS操作符
GROUPING SETS操作符是GROUP BY子句的进一步扩展。在Oracle Database 9i之前,使用GROUP BY子句一次只能显示单种分组结果,如果要生成多种分组统计结果,那么需要编写多条SELECT分组语句。从Oracle Database 9i开始,通过使用GROUPING SETS操作符,可以合并多个分组的统计结果,从而简化了多个分组操作。
select deptno, job, avg (sal)
from emp
group by grouping sets(deptno,job);
针对优化后的问题,可以使用如下的SQL查询语句来解决:
SELECT TO_CHAR(DATE_COL, 'yyyy-MM-dd') AS MONTHLY_DATE
FROM (
SELECT TRUNC(TO_DATE('2023-01-01', 'yyyy-MM-dd'), 'MM') + LEVEL - 1 AS DATE_COL
FROM DUAL
CONNECT BY LEVEL <= 12
)
WHERE DATE_COL = LAST_DAY(DATE_COL) OR TO_CHAR(DATE_COL, 'dd') = '27'
解释如下:
首先使用 TRUNC
函数将给定年份的1月1日转换为日期格式(使用 TO_DATE
函数),同时返回该月的第一天,接着使用 LEVEL
函数加上月份对应的序号,从而生成每个月的第一天。这里使用了 DUAL
表和 CONNECT BY
语句,以生成包含12个日期的虚拟表。
对于每个月的第一天,通过 LAST_DAY
函数可以计算出这个月的最后一天,而使用 TO_CHAR
函数可以将日期转换为字符串格式,从而获取这个月的最后一天的日期字符串。
还需要检查每个月是否存在27日,可以通过 TO_CHAR
函数将日期转换为字符串,并且使用 dd
格式化标识符来提取月份的日期部分。如果这个日期为27,则将其保存下来。
最终的结果是通过将每个月的最后一天和27号合并起来,在一个总的结果集中返回。我们使用外部查询语句来检查每个月的日期是否符合要求,并用 TO_CHAR
函数将它们转换为字符串,这样可以使用一个 SELECT
语句来返回结果集。
需要说明的是,这个查询语句的关键是使用了 CONNECT BY
语句和 LEVEL
函数来生成一列日期,然后使用各种函数来操作这些日期,从而得到符合要求的结果。需要特别注意的是,在Oracle数据库中,我们可以使用 DUAL
表来生成虚拟的行数据,DUAL
表只有一行,但是通过 CONNECT BY
语句可以不断生成更多的行,这样可以避免使用外部数据源,并且可以方便地生成任意数量的数据行。
SELECT
to_char(ADD_MONTHS(SYSDATE,-2),'yyyy/mm/dd'),
to_char(ADD_MONTHS(SYSDATE,-1),'yyyy/mm/dd'),
to_char(SYSDATE,'yyyy/mm/dd')
FROM dual