with语句的使用

并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。

实现上下文管理是通过enterexit这两个方法实现的。

class Query(object):

    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('Begin')
        return Query(self.name)

    def __exit__(self,exc_type,exc_value,traceback):
        if exc_type:
            print('Error')
        else:
            print('END')

    def query(self):
        print('Query info about %s...'% self.name)

with Query('Bob') as q:
    q.query()

例如with context_expression [as target(s)]:
with-body,

它的执行顺序是这样的

1.执行 context_expression,生成上下文管理器 context_manager

2.调用上下文管理器的 enter() 方法;如果使用了 as 子句,则将 enter() 方法的返回值赋值给 as 子句中的 target(s)

3.执行语句体 with-body

4.不管是否执行过程中是否发生了异常,执行上下文管理器的 exit() 方法,exit() 方法负责执行“清理”工作,如释放资源等。如果执行过程中没有出现异常,或者语句体中执行了语句 break/continue/return,则以 None 作为参数调用 exit(None, None, None) ;如果执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用 exit(exc_type, exc_value, exc_traceback)

5.出现异常时,如果 exit(type, value, traceback) 返回 False,则会重新抛出异常,让with 之外的语句逻辑来处理异常,这也是通用做法;如果返回 True,则忽略异常,不再对异常进行处理。

编写enterexit仍然很繁琐,因此Python的标准库contextlib提供了更简单的写法,上面的代码可以改写如下:

from contextlib import contextmanager

class Query(object):

    def __init__(self, name):
        self.name = name

    def query(self):
        print('Query info about %s...' % self.name)

@contextmanager
def create_query(name):
    print('Begin')
    q = Query(name)
    yield q
    print('End')

@contextmanager这个decorator接受一个generator,用yield语句把with … as var把变量输出出去,然后,with语句就可以正常地工作了:

with create_query('Bob') as q:
    q.query()

很多时候,我们希望在某段代码执行前后自动执行特定代码,也可以用@contextmanager实现。例如:

@contextmanager
def tag(name):
    print("<%s>" % name)
    yield
    print("</%s>" % name)

with tag("h1"):
    print("hello")
    print("world")

上述代码执行结果为:

<h1>
hello
world
</h1>

代码的执行顺序是:

with语句首先执行yield之前的语句,因此打印出<h1>;
yield调用会执行with语句内部的所有语句,因此打印出hello和world;
最后执行yield之后的语句,打印出</h1>。
因此,@contextmanager让我们通过编写generator来简化上下文管理。

@closing

如果一个对象没有实现上下文,我们就不能把它用于with语句。这个时候,可以用closing()来把该对象变为上下文对象。例如,用with语句使用urlopen():

from contextlib import closing
from urllib.request import urlopen

with closing(urlopen('https://www.python.org')) as page:
    for line in page:
        print(line)

closing也是一个经过@contextmanager装饰的generator,这个generator编写起来其实非常简单:

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()

它的作用就是把任意对象变为上下文对象,并支持with语句。

@contextlib还有一些其他decorator,便于我们编写更简洁的代码。

<think>嗯,用户之前问过关于Oracle中多对多递归查询的问题,现在又问了“with语句使用oracle”。我需要先回想一下之前的回答内容,确保这次回答的一致性和连贯性。之前详细解释了递归WITH子句在多对多关系中的应用,包括语法、示例和注意事项。这次用户可能想更全面地了解WITH语句在Oracle中的各种用途,而不仅仅是递归查询。 首先,我需要确认用户的具体需求。他们可能对WITH语句的基础用法、应用场景或高级功能感兴趣。可能用户在使用Oracle时遇到了需要优化查询、重用子查询或者处理复杂逻辑的情况,因此想深入了解WITH语句使用方法。 接下来,我需要整理WITH语句的不同用途。除了递归查询,Oracle的WITH子句(即公共表表达式,CTE)主要用于简化复杂查询,提高可读性,避免重复子查询。可能还需要提到它的临时视图特性,以及如何在同一个查询中多次引用同一个CTE。 然后,要考虑用户可能的疑问点。比如,WITH语句和临时表的区别,性能优化方面的考虑,或者如何避免常见的错误,比如循环引用或无限递归。特别是用户之前接触过递归查询,可能需要强调递归CTE和非递归CTE的不同之处,以及如何正确使用它们。 另外,用户可能希望看到具体的示例,特别是实际应用中的案例,比如分步统计、数据去重、层次查询等。这些例子能帮助他们更好地理解如何在实际项目中应用WITH语句。同时,需要确保示例的SQL语法正确,符合Oracle的标准,特别是版本兼容性,比如CYCLE子句在11g及以上版本才支持。 还要注意用户可能存在的误区。例如,WITH子句虽然可以提升可读性,但过度使用或不当使用可能导致性能问题。需要提醒用户合理使用,并结合索引、执行计划等优化手段。 最后,总结部分要简明扼要,强调WITH语句的优势和适用场景,帮助用户快速抓住重点。同时,可以推荐一些进一步学习的资源或最佳实践,方便用户深入探索。</think>以下是对Oracle中`WITH`语句(公共表表达式,CTE)的详细介绍,分步骤说明其使用方法和场景: --- ### 一、`WITH`语句基础 #### 1. **定义** - `WITH`语句(Common Table Expression,CTE)用于定义**临时命名查询结果集**,可在同一查询中多次引用。 - 类似“临时视图”,但仅存在于当前查询的生命周期内。 #### 2. **核心优势** - **简化复杂查询**:将多层嵌套的子查询拆分为逻辑块。 - **代码重用**:避免重复编写相同子查询。 - **递归支持**:处理树形/图结构数据(需配合`UNION ALL`)。 --- ### 二、基本语法 ```sql WITH cte_name1 (列1, 列2, ...) AS ( SELECT ... FROM ... -- 子查询1 ), cte_name2 AS ( SELECT ... FROM cte_name1 -- 子查询2(可引用其他CTE) ) SELECT * FROM cte_name2; -- 最终查询 ``` --- ### 三、典型应用场景 #### 1. **简化复杂查询** **示例**:统计部门平均工资及高于平均工资的员工 ```sql WITH dept_avg AS ( SELECT department_id, AVG(salary) avg_sal FROM employees GROUP BY department_id ) SELECT e.employee_id, e.salary, d.avg_sal FROM employees e JOIN dept_avg d ON e.department_id = d.department_id WHERE e.salary > d.avg_sal; ``` #### 2. **分步计算** **示例**:分阶段统计销售额 ```sql WITH region_sales AS ( SELECT region, SUM(amount) total FROM orders GROUP BY region ), top_regions AS ( SELECT region FROM region_sales WHERE total > 1000000 ) SELECT * FROM top_regions; ``` #### 3. **递归查询** **示例**:查询员工层级关系(树形结构) ```sql WITH employee_tree (employee_id, name, manager_id, level) AS ( -- 锚点:顶级管理者(无上级) SELECT employee_id, name, manager_id, 1 FROM employees WHERE manager_id IS NULL UNION ALL -- 递归:逐层向下查找下属 SELECT e.employee_id, e.name, e.manager_id, et.level + 1 FROM employees e JOIN employee_tree et ON e.manager_id = et.employee_id ) SELECT * FROM employee_tree; ``` --- ### 四、高级用法 #### 1. **多CTE串联** ```sql WITH temp1 AS (SELECT ...), temp2 AS (SELECT ... FROM temp1), temp3 AS (SELECT ... FROM temp2) SELECT * FROM temp3; ``` #### 2. **递归中的循环检测**(Oracle 11g+) ```sql WITH employee_tree (...) AS (...) CYCLE employee_id SET is_cycle TO 'Y' DEFAULT 'N' SELECT * FROM employee_tree WHERE is_cycle = 'N'; ``` #### 3. **MATERIALIZE提示强制物化**(性能优化) ```sql WITH /*+ MATERIALIZE */ temp AS (SELECT ...) SELECT * FROM temp; ``` --- ### 五、性能注意事项 1. **避免过度嵌套**:超过3层的CTE可能影响可读性。 2. **索引优化**:确保CTE中的JOIN字段已建索引。 3. **递归深度控制**:使用`WHERE level < N`限制层级。 4. **替代方案**:复杂场景可考虑临时表或内联视图。 --- ### 六、与子查询的对比 | 特性 | `WITH`语句 | 子查询 | |---------------------|---------------------|-------------------| | 可读性 | 高(分块命名) | 低(嵌套复杂) | | 重用性 | 可多次引用 | 不可重用 | | 递归支持 | 支持 | 不支持 | | 执行计划优化 | 可能被优化器物化 | 通常内联执行 | --- ### 七、实际案例 #### 场景:统计部门及子部门的总人数 ```sql WITH dept_hierarchy (department_id, parent_id, level) AS ( SELECT department_id, parent_id, 1 FROM departments WHERE parent_id IS NULL -- 根部门 UNION ALL SELECT d.department_id, d.parent_id, dh.level + 1 FROM departments d JOIN dept_hierarchy dh ON d.parent_id = dh.department_id ) SELECT dh.department_id, COUNT(e.employee_id) AS total_employees FROM dept_hierarchy dh LEFT JOIN employees e ON dh.department_id = e.department_id GROUP BY dh.department_id; ``` --- ### 总结 Oracle的`WITH`语句通过模块化查询逻辑显著提升代码可维护性,尤其适用于: 1. 递归数据处理(如组织结构、BOM表) 2. 多步骤复杂计算 3. 需要重复引用的子查询场景 建议在涉及多层嵌套或递归时优先使用`WITH`语句,并结合执行计划分析优化性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值