记一次pgsql中with as语法的使用以及with as介绍
最近在做一个项目,遇到了需要统计每个中队下当日警员的行驶的总里程数与巡逻时长,该需求就涉及到需要使用with as 子句。涉及到以下三个表:duty_statistic_worklog、duty_person、duty_department。其中部门从属关系是:交警支队下有12个大队,每个大队下分别挂若干个中队,中队下边挂警员。
统计当日警员巡逻的总里程数sql语句:
java
with a as (
select
t3.bmbh as sjbmbh,
cast('rcqw' as varchar) as menu2,
cast('02' as varchar) as menu3,
sum(t1.zqlc) as cnt
from
(
select
*
from
duty_statistic_worklog
where
bmbh in (:deptIds)
and tjsj >= :startTime
and tjsj <= :endTime ) t1
inner join (
select
*,
case
when ywlx not like '%@tq@%'
and jylx != '0' then '0'
when ywlx not like '%@tq@%'
and jylx = '0' then '1'
when ywlx like '%@tq@%'
and jylx != '0' then '2'
when ywlx like '%@tq@%'
and jylx = '0' then '3'
end as jylx_new
from
duty_person ) t2 on
t1.jh = t2.jh
and jylx_new in (:policeTypes)
right join duty_department t3 on
t1.bmbh = t3.bmbh
group by
t3.bmbh),
relation as (
select
bmbh,sjbmbh
from
duty_department
)
select t.sjbmbh,t.menu2,t.menu3,sum(t.subCnt) as cnt
from (
select t1.*,case when t3.cnt isnull then t1.cnt else t3.cnt end as subCnt from
a t1 left join relation t2 on t1.sjbmbh = t2.sjbmbh
left join a t3 on t2.bmbh = t3.sjbmbh ) t
group by t.sjbmbh,t.menu2,t.menu3
统计警员当日巡逻的总时长sql语句:
java
with a as (
select
t3.bmbh as sjbmbh,
cast('rcqw' as varchar) as menu2,
cast('03' as varchar) as menu3,
sum(t1.zqsc) as cnt
from
(
select
*
from
duty_statistic_worklog
where
bmbh in (:deptIds)
and tjsj >= :startTime
and tjsj <= :endTime ) t1
inner join (
select
*,
case
when ywlx not like '%@tq@%'
and jylx != '0' then '0'
when ywlx not like '%@tq@%'
and jylx = '0' then '1'
when ywlx like '%@tq@%'
and jylx != '0' then '2'
when ywlx like '%@tq@%'
and jylx = '0' then '3'
end as jylx_new
from
duty_person ) t2 on
t1.jh = t2.jh
and jylx_new in (:policeTypes)
right join duty_department t3 on
t1.bmbh = t3.bmbh
group by
t3.bmbh),
relation as (
select
bmbh,sjbmbh
from
duty_department
)
select t.sjbmbh,t.menu2,t.menu3,sum(t.subCnt) as cnt
from (
select t1.*,case when t3.cnt isnull then t1.cnt else t3.cnt end as subCnt from
a t1 left join relation t2 on t1.sjbmbh = t2.sjbmbh
left join a t3 on t2.bmbh = t3.sjbmbh ) t
group by t.sjbmbh,t.menu2,t.menu3
接下来主要介绍with as是什么、做什么以及怎么用。
with as定义
with as 短语,也叫做子查询部分(subquery factoring),是用来定义一个SQL片段,该片段会被整个SQL语句所用到。这个语句算是公用表表达式(CTE)。
比如 with A as (select * from table),这个语句的意思就是先执行 select * from table 得到一个结果,将这个结果记录为A,在执行 select * from A,A表只是一个别名。
也就是将重复用到的大批量的SQL语句,放到 with as 中,加一个别名,在后面用到的时候就可以直接用。对于大批量的SQL数据,起到优化的作用。
with as使用场景
在postgresql中,with子句提供了一种编写辅助语句的方法,以便在更大的查询中使用。with子句有助于将复杂的大型查询分解为更简单的表单,便于阅读。这些语句通常被称为通用表表达式(Common Table Express,CTE),也可以当做一个为查询而存在的临时表。
with子句是在多次执行查询时特别有用,允许我们在查询中通过它的名称(可能是多次)引用它。with子句在使用前必须先定义。
当一个查询的结果希望被重用或者这个结果集在另外一个查询中作为数据源,查询条件等使用with语句是比较方便的。当然大部分的with语句是可以被表连接等其他的方式所替代的,只是with语句比较方便,逻辑也很清晰。
with as使用
在with子句中的每一个辅助语句。可以是一个select、insert、update或delete,并且with子句本身也可以被附加到一个主语句,主语句也可以是select、insert、update或delete。
with中的查询
比如,需要展示在高销售区域每种产品的销售总额。Sql语句如下:
java
WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
), top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region,
product,
SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
regional_sales---获取每个区域的销售量总和 top_regions---获取销售额超过平均销售额的区域
with中的使用数据修改语句
with中可以不仅可以使用SELECT语句,同时还能使用DELETE,UPDATE,INSERT语句。因此,可以使用with,在一条SQL语句中进行不同的操作,如下例所示。
java
WITH moved_rows AS (
DELETE FROM products
WHERE
"date" >= '2010-10-01'
AND "date" < '2010-11-01'
RETURNING *
)
INSERT INTO products_log
SELECT * FROM moved_rows;
通过with中的DELETE语句从products表中删除了一个月的数据,并通过RETURNING子句将删除的数据集赋给moved_rows这一CTE,最后在主语句中通过INSERT将删除的商品插入products_log中。
with as的优点
① SQL可读性增强。比如对于特定with子查询去有意义的名字等。
② with子查询只执行一次,将结果存储在用户临时表空间中,可引用多次,增强性能。
with as使用注意事项
① 使用with子句可以让子查询重用相同的with查询块,通过select调用(with子句只能被select查询块引用),一般在with查询用到多次情况下。在引用的select语句之前定义,同级只能定义with关键字只能使用一次,多个逗号分隔。
② with子句的返回结果存到用户的临时表空间中,只做一次查询,反复使用,提高效率。
③ 在同级select前有多个select查询定义的时候,第一个用with,后面的不用with,并用逗号隔开。
④ 最后一个with子句与下面的查询之间不能有逗号,只能通过右括号分割,with子句的查询必须用括号括起来。
⑤ 如果定义了with子句,而查询中不使用,那么会报ora-32035错误:未引用在with子句中定义的查询名。(至少1个with查询的name未被引用,解决方法是移除未被引用的with查询),注意:只要后面有引用的就可以,不一定非要在主查询中引用,比如后面的with查询也引用了,也是可以的。
⑥ 前面的with子句定义的查询在后面的with子句中可以使用。但是一个with子句内部不能嵌套with子句。
⑦ 当一个查询块名字和一个表名或其他的对象相同时,解析器从内向外搜索,优先使用子查询模块名字。
⑧ with查询的结果列有别名,引用的时候必须使用别名或*。
⑨ with中的数据修改语句会被执行一次,并且肯定会完全执行,无论主语句是否读取或者是否读取所有其输出。而with中的select语句则只输出主语句中所需要记录数。
⑩ with中使用多个子句时,这些子句和主语句会并行执行,所以当存在多个修改子语句修改相同的记录时,它们的结果不可预测。
⑪ 所有的子句所能"看"到的数据集是一样的,所以它们看不到其它语句对目标数据集的影响。这也缓解了多子句执行顺序的不可预测性造成的影响。
⑫ 如果在一条SQL语句中,更新同一记录多次,只有其中一条会生效,并且很难预测哪一个会生效。
⑬ 如果在一条SQL语句中,同时更新和删除某条记录,则只有更新会生效。
⑭ 目前,任何一个被数据修改CTE的表,不允许使用条件规则,和ALSO规则以及INSTEAD规则。