如何用Python re模块检测Oracle存储过程与函数中的表名
re Great question! Your initial regex is a solid starting point, but it falls short of handling all the Oracle FROM clause variations you listed—like JOIN syntax, schema-qualified tables, quoted identifiers, and multi-table comma-separated joins. Let's break down a more robust solution step by step.
1. First: Address Limitations of Your Current Code
Your current pattern FROM \w+ \w* [WHERE|,|;]* has a few key gaps:
- It doesn't account for schema-qualified tables (e.g.,
SCOTT.EMP) - It can't handle quoted table names (Oracle allows double-quoted identifiers like
"EMP_TABLE") - It misses tables referenced in
JOINclauses (e.g.,FROM EMP e JOIN DEPT d ON e.DEPTNO = d.DEPTNO) - It doesn't ignore comments (you might accidentally match
FROMin-- FROM test_tableor/* FROM test_table */)
2. Step-by-Step Robust Regex Design
Let's build a regex that covers all your required cases, plus common Oracle edge cases:
a. Define Valid Oracle Identifiers
Oracle table names can take three forms, so we need a pattern that matches all:
- Plain identifiers: Start with a letter, followed by letters, numbers, or underscores
- Schema-qualified:
schema.table - Quoted:
"My-Table-With-Special-Chars"
We can represent this with:
(?:(?:"[^"]+")|(?:[A-Za-z_][A-Za-z0-9_]*\.)?[A-Za-z_][A-Za-z0-9_]*)
(?:"[^"]+"): Matches quoted identifiers(?:[A-Za-z_][A-Za-z0-9_]*\.)?: Optional schema prefix[A-Za-z_][A-Za-z0-9_]*: Plain identifier
b. Match FROM and JOIN Table References
We need to capture tables from both the initial FROM clause and any subsequent JOIN (INNER/LEFT/RIGHT/FULL) clauses. The full pattern (case-insensitive, allows whitespace/newlines) looks like:
(?:FROM|JOIN)\s+(?P<table>(?:(?:"[^"]+")|(?:[A-Za-z_][A-Za-z0-9_]*\.)?[A-Za-z_][A-Za-z0-9_]*))(?:\s+(?P<alias>(?:"[^"]+")|[A-Za-z_][A-Za-z0-9_]*))?
c. Preprocess: Remove Comments First
Before running the regex, strip Oracle-style comments to avoid false matches:
- Line comments:
-- ...until the end of the line - Block comments:
/* ... */(can span multiple lines)
3. Full Python Implementation
Here's a complete function that handles all these cases:
import re def extract_oracle_table_names(procedure_code): # Step 1: Remove block comments /* ... */ code_no_block_comments = re.sub(r'/\*.*?\*/', '', procedure_code, flags=re.DOTALL) # Step 2: Remove line comments -- ... code_no_comments = re.sub(r'--.*$', '', code_no_block_comments, flags=re.MULTILINE) # Step 3: Regex to match FROM/JOIN table references table_pattern = re.compile( r'(?:FROM|JOIN)\s+(?P<table>(?:(?:"[^"]+")|(?:[A-Za-z_][A-Za-z0-9_]*\.)?[A-Za-z_][A-Za-z0-9_]*))(?:\s+(?P<alias>(?:"[^"]+")|[A-Za-z_][A-Za-z0-9_]*))?', flags=re.IGNORECASE | re.DOTALL ) # Extract all table names, deduplicate table_matches = table_pattern.finditer(code_no_comments) table_names = {match.group('table').strip() for match in table_matches} return sorted(table_names) # Example usage sample_procedure = """ CREATE OR REPLACE PROCEDURE get_emp_data IS CURSOR emp_cursor IS SELECT e.EMPNO, e.ENAME, d.DNAME FROM SCOTT.EMP e JOIN SCOTT.DEPT d ON e.DEPTNO = d.DEPTNO WHERE e.SAL > 5000; -- This is a comment: FROM dummy_table /* Another comment with FROM test_table */ BEGIN -- Procedure logic here END; """ print(extract_oracle_table_names(sample_procedure)) # Output: ['SCOTT.DEPT', 'SCOTT.EMP']
4. Additional Enhancements (Optional)
- Handle Subqueries: If you want to exclude tables from subqueries (since you asked for tables used to "get data"), you can modify the regex to avoid matching inside
(SELECT ...). This gets trickier, but you can use negative lookaheads to skip subquery contexts. - Case Normalization: Convert all table names to uppercase (Oracle defaults to uppercase unless quoted) for consistency.
- Alias Mapping: If you want to track which aliases map to which tables, return a dictionary instead of a set.
内容的提问来源于stack exchange,提问作者JaviOverflow

