Python提取含笛卡尔连接SQL的表与列名(Python2.7报错求助)
Hey there! Let's break this down into two clear parts: fixing that frustrating error, then building the SQL table/column extractor you need.
First: Squashing the "Bool object not callable" Error
That error almost always pops up when you accidentally treat a boolean variable like a function. For example, if you had code like this:
is_parsed = True if is_parsed(): # Oops! is_parsed is a boolean, not a function print("Parsing done")
Python 2.7 throws this error because you're trying to "call" a boolean value like it's a method. The fix is simple—just remove the parentheses after the boolean variable:
is_parsed = True if is_parsed: print("Parsing done")
Double-check your code for any places where you added extra parentheses to a boolean variable or expression—that's almost certainly the root cause here.
Second: Extracting Table Names & Columns from Cartesian Join SQL
Parsing SQL manually with regex is a huge headache (especially with subqueries like your example), so we'll use the sqlparse library—it handles SQL syntax properly without the mess of regex. Note that for Python 2.7, you'll need an older version of sqlparse (0.4.4 is the last release that supports Python 2.x).
Step 1: Install sqlparse
Run this in your terminal:
pip install sqlparse==0.4.4
Step 2: The Extractor Code
Here's a script that parses your SQL, pulls out table names (including subquery aliases) and column names reliably:
import sqlparse from sqlparse.sql import IdentifierList, Identifier from sqlparse.tokens import Token def extract_tables_and_columns(sql): parsed = sqlparse.parse(sql)[0] tables = set() columns = set() # Extract columns from SELECT clause select_clause = None for token in parsed.tokens: if token.ttype is None and token.value.upper() == 'SELECT': idx = parsed.tokens.index(token) select_clause = parsed.tokens[idx + 1] break if select_clause: for item in select_clause.get_identifiers(): # Handle aliased columns (e.g., SUM(orders.amount) AS total_amt) if isinstance(item, Identifier): col_name = item.get_real_name() if '.' in col_name: columns.add(col_name) else: columns.add(col_name) else: # Extract columns wrapped in functions func_str = str(item) if '(' in func_str and ')' in func_str: inner_col = func_str.split('(')[1].split(')')[0].strip() if '.' in inner_col: columns.add(inner_col) # Extract tables from FROM clause (including Cartesian joins and subqueries) from_clause = None for token in parsed.tokens: if token.ttype is None and token.value.upper() == 'FROM': idx = parsed.tokens.index(token) from_clause = parsed.tokens[idx + 1] break if from_clause: def extract_table_identifiers(token): if isinstance(token, IdentifierList): for item in token.get_identifiers(): extract_table_identifiers(item) elif isinstance(token, Identifier): # Handle subqueries with aliases if token.has_alias(): tables.add(token.get_alias()) # Recursively parse tables inside the subquery subquery = token.token_first() if subquery and subquery.value == '(': sub_sql = str(token.token_next(1)).strip(')') sub_tables, _ = extract_tables_and_columns(sub_sql) tables.update(sub_tables) else: tables.add(token.get_real_name()) elif token.ttype is Token.Punctuation and token.value == ',': pass else: # Dig into nested tokens (like subquery parentheses) if hasattr(token, 'tokens'): for sub_token in token.tokens: extract_table_identifiers(sub_token) extract_table_identifiers(from_clause) return sorted(tables), sorted(columns) # Test with your sample SQL sample_sql = """SELECT suppliers.supplier_name, subquery1.total_amt FROM suppliers , (SELECT supplier_id, SUM(orders.amount) AS total_amt FROM orders GROUP BY supplier_id) subquery1 WHERE subquery1.supplier_id = suppliers.supplier_id;""" tables, columns = extract_tables_and_columns(sample_sql) print("Extracted Tables:") for table in tables: print(f"- {table}") print("\nExtracted Columns:") for col in columns: print(f"- {col}")
Step 3: What This Does
- Avoids the boolean error: The code doesn't make the mistake of calling boolean values, so you won't hit that issue here.
- Extracts tables: Pulls out regular tables (
suppliers,orders) and subquery aliases (subquery1), and even recursively parses subqueries to grab tables inside them. - Extracts columns: Captures columns with table prefixes (
suppliers.supplier_name,orders.amount) and aliased columns from subqueries (subquery1.total_amt).
Output for Your Sample SQL
When you run the script, you'll get:
Extracted Tables: - orders - subquery1 - suppliers Extracted Columns: - orders.amount - subquery1.supplier_id - subquery1.total_amt - suppliers.supplier_id - suppliers.supplier_name
Quick Notes
- This code handles your specific case smoothly, but for ultra-complex SQL (like deeply nested subqueries), you might need to tweak the parsing logic slightly.
- Python 2.7 is end-of-life, so if you can, consider upgrading to Python 3.x for better support—but this code works perfectly for your 2.7 requirement.
内容的提问来源于stack exchange,提问作者goodbaduglyboy

