python sqlparse解析sql得到表名

sql = """
select K.a,K.b from (select H.b from (select G.c from (select F.d from
(select E.e from A, B, C, D, E), F), G), H), I, J, K order by 1,2;
"""

import sqlparse
from sqlparse.sql import IdentifierList, Identifier, Where
from sqlparse.tokens import Keyword, DML


def is_subselect(parsed):
    if not parsed.is_group:
        return False
    for item in parsed.tokens:
        if item.ttype is DML and item.value.upper() == 'SELECT':
            return True
    return False


def extract_from_part(parsed):
    from_seen = False
    for item in parsed.tokens:
        if from_seen:
            if is_subselect(item):
                for x in extract_from_part(item):
                    yield x
            elif item.ttype is Keyword:
                raise StopIteration
            else:
                yield item
        elif item.ttype is Keyword and item.value.upper() == 'FROM':
            from_seen = True


def extract_table_identifiers(token_stream):
    for item in token_stream:
        if isinstance(item, IdentifierList):
            for identifier in item.get_identifiers():
                yield identifier.get_name()
        elif isinstance(item, Identifier):
            yield item.get_name()
        # It's a bug to check for Keyword here, but in the example
        # above some tables names are identified as keywords...
        elif item.ttype is Keyword:
            yield item.value


def extract_tables(sql):
    stream = extract_from_part(sqlparse.parse(sql)[0])
    return list(extract_table_identifiers(stream))

if __name__ == '__main__':
    print 'Tables: %s' % ', '.join(extract_tables(sql))

 

<think>我们面对的任务是从SQL查询语句中提取。根据用户需求,我们需要使用Python的正则达式来实现。考虑到SQL语句的多样性,可能出现在不同的上下文中。例如:1.简单的SELECT语句:SELECT*FROMtable_name2.带有模式(schema)的:SELECT*FROMschema.table_name3.使用方括号或引号引起来的(常见于SQLServer等):SELECT*FROM[schema].[table_name]或SELECT*FROM"schema"."table_name"4.多连接:SELECT*FROMtable1,table2或SELECT*FROMtable1JOINtable25.子查询:这种情况可能比较复杂,我们可能暂时不考虑子查询中的(因为子查询中的可能是临时或者别)我们的目标是提取FROM关键字后面以及JOIN关键字后面的。但是,请注意,SQL语句可能非常复杂,包括子查询、嵌套查询等,而正则达式处理嵌套结构是有局限的。因此,我们这里主要考虑相对简单的SQL语句,或者至少是FROM子句和JOIN子句相对直接的情况。根据引用[3]中的例子,我们可以观察到可能以多种形式出现:-Config-[dbo].IMEIUser-dbo.LotteryLog-[GreenPrize]-[dbo].[Config]-dbo.[Prize]-[DBName].[dbo].[Config]我们设计正则达式的思路:1.匹配FROM或JOIN关键字(注意:JOIN可能有多种,如INNERJOIN,LEFTJOIN等,所以我们匹配关键字后面跟着的JOIN)2.匹配关键字后面的部分。部分可能由以下组成:-可选的数据库和模式(用点分隔),每个部分都可以用方括号或引号括起来,也可以不用。-本身也可以用方括号或引号括起来,或者直接是标识符。标识符的规则:通常由字母、数字、下划线等组成,但有时也会包含其他字符(如空格),所以当包含空格或特殊字符时,通常会用方括号或引号括起来。我们可以尝试分步匹配,但为了简化,我们可以设计一个匹配(包括可能的限定符)的正则达式。一个(包括可能的数据库、模式)的组成部分可以这样匹配:(?:\[[^\]]+\]|\"[^\"]+\"|\w+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|\w+))*解释:第一部分:一个标识符,可以是:\[[^\]]+\]:匹配方括号内的任何非右方括号字符|\"[^\"]+\":或者双引号内的任何非双引号字符|\w+:或者一个或多个单词字符(字母、数字、下划线)后面可以跟零个或多个:点号分隔的另一个标识符(同样可以是方括号、引号或单词字符)然后,我们需要匹配FROM或JOIN关键字后面的这个模式。注意,关键字后面可能有空格,后面可能有空格或逗号等。但是,我们也要注意避免匹配到子查询中的FROM或JOIN。这比较困难,所以我们假设用户提供的SQL语句中,最外层的FROM和JOIN是我们关心的。另外,我们可能还需要考虑,例如:FROMtable1ASt1,但我们只需要,所以别可以忽略。同样,我们也要忽略掉ON等条件子句。因此,我们可以这样写正则达式:(?:FROM|JOIN)\s+((?:\[[^\]]+\]|\"[^\"]+\"|\w+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|\w+))*)但是,这个达式会匹配整个限定(如db.schema.table)。但是,我们可能只需要部分?或者保留完整路径?根据需求,我们可以选择保留完整路径或者只取最后一部分。另外,注意这个达式会匹配到JOIN前面的类型(如INNERJOIN),所以我们可能需要匹配各种JOIN:(?:FROM|(?:INNER\s+)?JOIN|LEFT\s+JOIN|RIGHT\s+JOIN|FULL\s+JOIN)\s+但是,这样写会很长。我们可以简化:因为所有JOIN前面都有关键字JOIN,所以我们匹配:\b(?:FROM|JOIN)\b但是,这样会漏掉LEFTJOIN等,所以我们改为:\b(?:FROM|(?:INNER\s+)?JOIN|LEFT\s+OUTER?\s+JOIN|RIGHT\s+OUTER?\s+JOIN|FULL\s+OUTER?\s+JOIN|CROSS\s+JOIN)\b这样写比较全面,但可能过于复杂。另一种思路是:我们匹配FROM和任何以JOIN结尾的关键字(但注意FROM不是以JOIN结尾)。我们可以拆分成两部分:先匹配FROM,然后匹配各种JOIN关键字。但是,为了简单,我们这里先考虑匹配FROM和JOIN(包括前面的修饰符)作为关键字。实际上,我们可以先提取FROM和JOIN后面的,而不考虑JOIN的类型,因为总是在这些关键字后面。因此,我们可以这样:\b(?:FROM|(?:[A-Z]+\s+)*JOIN)\s+解释:匹配FROM或者任何前面可能有大写单词(如INNER,LEFT等)后面跟着JOIN的关键字。注意:这个正则可能会匹配到一些不是关键字的地方,但考虑到SQL语句通常是大写,或者我们可以用re.IGNORECASE标志忽略大小写。另外,注意后面可能有别,比如:FROMtable1t1,或者JOINtable2ASt2。我们需要跳过别。别通常是一个单词(或带引号/方括号的标识符),并且前面可能有AS关键字。因此,在匹配后,我们可能会遇到:-空格然后一个单词(别)-或者逗号(示下一个)-或者ON关键字(开始连接条件)-或者WHERE等我们不需要匹配别,所以我们在匹配后,应该忽略后面的别部分。因此,我们可以调整:在匹配后,如果后面跟着AS或一个标识符(别),我们就不捕获这个别。所以,整个正则达式可以设计为:\b(?:FROM|(?:[A-Z]+\s+)*JOIN)\s+((?:\[[^\]]+\]|\"[^\"]+\"|\w+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|\w+))*)\s*(?:AS\s+\w+|\s+\w+)?(?=\s+|,|$|ON)解释:-\b(?:FROM|(?:[A-Z]+\s+)*JOIN)\s+:匹配关键字(FROM或各种JOIN)后面至少一个空格-((?:\[[^\]]+\]|\"[^\"]+\"|\w+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|\w+))*):捕获组1,匹配(可能带限定符)-\s*(?:AS\s+\w+|\s+\w+)?:匹配可选的别(可能有AS,也可能没有)-(?=\s+|,|$|ON):正向先行断言,确保后面是空格、逗号、结束或者ON关键字(这样确保我们不会匹配过多)但是,这个正则达式仍然可能不够健壮,因为SQL语句的复杂性。另外,我们还需要注意,同一个SQL语句中可能有多个(例如逗号分隔的多),所以我们需要匹配多个。对于逗号分隔的情况,例如:FROMtable1,table2,table3我们的正则达式在匹配完table1后,由于逗号不在我们后面的断言中(我们断言了逗号),所以下一次匹配可以从逗号后面的table2开始?但是,我们的关键字只匹配FROM和JOIN,所以逗号后面的不会被匹配到。因此,我们需要特别处理逗号分隔的情况。我们可以修改正则达式,在FROM后面匹配多个(逗号分隔)。但是,这会使正则达式变得复杂。另一种思路:我们分两步:第一步:定位FROM子句(直到遇到WHERE、GROUPBY等关键字或结束)第二步:在这个子句中提取(包括JOIN子句)但是,为了简单起见,我们这里先处理每个都由FROM或JOIN引入的情况(包括逗号分隔的情况,我们可以通过多次匹配,但注意逗号分隔的前面没有关键字)。所以,我们调整:在FROM关键字后面,我们可能匹配多个(用逗号分隔),那么我们可以把逗号后面的看作是隐式的JOIN?所以我们可以这样:(?:FROM|JOIN|,)\s+((?:\[[^\]]+\]|\"[^\"]+\"|\w+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|\w+))*)\s*(?:AS\s+\w+|\s+\w+)?(?=\s+|,|$|ON)这里,我们在关键字列中加入逗号。这样,逗号后面跟着的也会被匹配。但是,注意逗号可能出现在SELECT子句中,所以这样可能会误匹配。因此,我们可能需要限制:逗号匹配只发生在FROM子句中。由于复杂性,我们这里采用一种折中:先匹配FROM和JOIN关键字后面的,然后单独处理逗号分隔的情况。我们可以先通过正则找到FROM子句的范围,然后在这个范围内提取逗号分隔的。但是,为了快速实现,我们假设用户提供的SQL语句中,都是由FROM或JOIN引入的(即不使用逗号分隔的旧式连接)。如果用户使用逗号分隔,那么只有第一个前面有FROM,后面的前面是逗号,所以我们还是需要匹配逗号。因此,我们决定使用:\b(?:FROM|(?:[A-Z]+\s+)*JOIN|,)\s+((?:\[[^\]]+\]|\"[^\"]+\"|\w+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|\w+))*)\s*(?:AS\s+\w+|\s+\w+)?(?=\s+|,|$|ON)注意:这个正则达式可能会匹配到SELECT子句中的逗号(如果逗号后面跟着一个空格和一个标识符,而这个标识符又符合的模式,就可能被误认为)。因此,我们最好先定位FROM子句。考虑到时间,我们采用一个相对简单的方法:我们只考虑在FROM关键字出现之后,且在后续关键字(如WHERE、GROUPBY、HAVING、ORDERBY、LIMIT等)之前的字符串中提取。步骤:1.使用正则达式定位FROM子句的范围:从FROM开始,直到遇到WHERE等关键字或语句结束。2.在这个范围内,使用正则达式提取(包括JOIN和逗号后面的)。但是,这样处理仍然复杂,因为子查询的存在可能会干扰。鉴于问题的复杂性,我们这里先实现一个基础版本,它可能无法处理所有情况,但对于大多数简单查询有效。我们最终的正则达式(不考虑子查询,只匹配显式的FROM、JOIN和逗号):pattern=r'\b(?:FROM|(?:[A-Z]+\s+)*JOIN|,)\s*((?:\[[^\]]+\]|\"[^\"]+\"|\w+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|\w+))*)'然后,我们使用re.IGNORECASE标志。但是,我们还没有处理别:别部分我们不想捕获,所以我们在捕获组中只包含部分,而别部分不包含在捕获组中。另外,注意前后可能有空格,我们在捕获后可以去掉空格。我们写一个Python函数来实现:步骤:1.使用re.findall()匹配整个SQL语句,找出所有符合模式的部分。2.返回捕获组1(即)的列。但是,我们可能会匹配到重复的,或者同一个多次出现(例如自连接),这取决于需求,我们可能要去重,但用户没有要求,我们先返回所有出现的。另外,注意我们匹配到的可能是带模式或数据库的完整称,用户可能需要拆分,但需求只是提取,所以我们返回完整称。我们测试几个例子(基于引用[3]中的例子)。例如:sql1="SELECT*FROMConfig"sql2="SELECT*FROM[dbo].IMEIUser"sql3="SELECT*FROMdbo.LotteryLog"sql4="SELECT*FROM[GreenPrize]"sql5="SELECT*FROM[dbo].[Config]"sql6="SELECT*FROMdbo.[Prize]"sql7="SELECT*FROM[DBName].[dbo].[Config]"sql8="SELECT*FROMtable1,table2"sql9="SELECT*FROMtable1INNERJOINtable2ONtable1.id=table2.id"注意:在sql8中,我们希望提取table1和table2。另外,注意子查询的情况(我们暂时不处理):sql10="SELECT*FROM(SELECT*FROMtemp)ASt"在sql10中,我们不应该提取temp(因为它在子查询中),但是我们的正则达式可能会匹配到子查询中的FROM。所以我们需要避免。如何避免?我们可以尝试跳过括号内的内容。但是,正则达式处理嵌套括号很困难。因此,我们这里先不考虑子查询,如果用户有需求,我们可以建议使用SQL解析器(如sqlparse)而不是正则达式。现在,我们编写代码。另外,注意我们的正则达式可能会匹配到字符串常量中的内容,例如:"SELECT*FROMtable1WHEREcomment='FROMtable2'"这里,'FROMtable2'是一个字符串,不应该被匹配。因此,我们最好先去掉字符串常量。但同样,这会使问题复杂化。因此,我们明确这个正则达式适用于简单的SQL语句,不包含子查询和字符串常量中的类似模式。代码实现:importredefextract_table_names(sql):#正则达式模式#注意:我们使用re.IGNORECASE忽略关键字大小写pattern=r'\b(?:FROM|(?:[A-Z]+\s+)*JOIN|,)\s*((?:\[[^\]]+\]|\"[^\"]+\"|[^\s\.,;()]+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|[^\s\.,;()]+))*)'#解释:我们修改了部分,因为原来的\w+不能匹配包含点号的(实际上不应该包含点号,但限定是多个部分用点连接)#但是,我们上面的模式已经可以匹配多个点连接的限定。但是,我们修改了非限定符部分:原来用\w+,现在用[^\s\.,;()]+,示除了空格、点、逗号、分号、括号以外的字符。#为什么这样改?因为原来的\w+不能匹配包含其他字符(如连字符)的,而用引号或方括号括起来的我们已经单独匹配了。#但是,对于没有引号或方括号的,我们允许非空白、非点、非逗号等字符。注意,也可能包含下划线、数字等,所以用[^\s\.,;()]是合理的。#但是,这个模式可能会匹配到其他东西,比如函数,但这里我们假设在FROM/JOIN/,后面的就是我们想要的。matches=re.findall(pattern,sql,re.IGNORECASE)#返回去重后的列(但保留顺序,因为同一个出现多次可能是重复,但用户可能想知道多次出现)#用户需求是提取,所以返回所有提取到的(包括重复)returnmatches测试上面的例子。但是,注意在sql2中,我们提取到的是"[dbo].IMEIUser",这是一个整体。用户可能需要分别获得每个部分?但我们需求是提取,所以整个作为?或者,通常我们认为最后一部分是。但用户没有要求,我们返回完整称。另外,在sql5中,我们提取到的是"[dbo].[Config]",同样是一个整体。如果用户希望只取部分(即最后一个标识符),我们可以对每个匹配结果进行拆分,取最后一个点之后的部分(并去掉方括号或引号)。但这不是当前需求,所以先返回完整称。我们测试一下:对于sql1:"SELECT*FROMConfig"->返回['Config']对于sql2:"SELECT*FROM[dbo].IMEIUser"->返回['[dbo].IMEIUser'],但实际上我们可能希望得到IMEIUser()和dbo(模式)。但需求是提取,这个整体就是的限定。如果用户只需要(不带模式),我们可以再处理:defextract_table_names(sql,only_table_name=False):#...先得到匹配的列ifonly_table_name:result=[]fornameinmatches:#将整个限定按点分割,取最后一部分parts=re.split(r'\s*\.\s*',name)last_part=parts[-1]#去掉方括号或引号iflast_part.startswith('[')andlast_part.endswith(']'):last_part=last_part[1:-1]eliflast_part.startswith('"')andlast_part.endswith('"'):last_part=last_part[1:-1]result.append(last_part)returnresultelse:returnmatches但我们先不实现这个,因为需求没有明确。现在,我们根据上面的例子测试。注意:在sql8:"SELECT*FROMtable1,table2"应该返回['table1','table2']。但是,我们的正则达式在匹配','的时候,注意模式中有一个逗号,所以匹配','时,后面的空格和table2会被捕获。但是,我们的模式中,关键字后面有\s*,所以逗号后面的空格会被跳过。但是,在sql8中:首先匹配FROM,然后空格,然后table1->第一个匹配是'table1'然后,逗号后面的空格,然后table2->第二个匹配是'table2'所以,应该可以匹配到两个。测试sql9:"SELECT*FROMtable1INNERJOINtable2ON..."->应该匹配到table1(由FROM引入)和table2(由JOIN引入)。但是,注意在FROM和table1之间有一个空格,然后INNERJOIN后面也有空格,所以可以匹配到。但是,我们的正则达式在FROM和JOIN之间没有匹配,所以只匹配FROM后面的table1和JOIN后面的table2。所以,我们编写代码并测试。另外,注意别问题:我们之前的设计中,在后面有别,我们用非捕获组跳过了别。但是,在正则达式中,我们并没有在捕获组后面加入别的匹配,因为我们发现别可能干扰的匹配(如果别写在一起,我们可能会捕获整个和别)。所以我们在上面模式中,在捕获组后面加入了别的匹配(但是非捕获的),并且使用正向先行断言确保后面是空格等。但是,在findall中,我们只返回捕获组1,所以别不会被返回。但是,我们上面的模式中并没有加入别的匹配(我们之前设计了一个带别的模式,但后来简化了)。为了简单,我们暂时不考虑别,因为别不影响的提取(在别前面)。因此,我们使用上面的简化模式(没有别匹配部分)也能提取,因为别之后,而我们匹配后,别不在捕获组中。但是,如果后面紧跟别(没有空格),例如:FROMtable1t1,那么我们的模式会匹配到"table1t1"吗?不会,因为我们的模式是:非空格、逗号、点等字符组成的序列,所以table1匹配后,空格后面的t1不会被包含在捕获组中。所以,我们使用上面的模式。但是,我们修改一下:在模式中,我们允许的字符集是[^\s\.,;()],所以内部不能有空格。因此,后面如果有空格,那么空格后面的别就不会被包含。所以,我们不需要特别处理别。开始写代码,并测试。注意:我们还需要考虑SQL语句中可能有注释,注释中的FROM/JOIN也会被匹配。因此,理想情况下,我们应该去掉注释。但这里为了简单,先不考虑。我们写一个函数,然后测试。另外,注意分号:SQL语句可能以分号结束,所以我们在匹配时,后面可能遇到分号(作为结束),所以我们在正向先行断言中加入分号。修改模式:pattern=r'\b(?:FROM|(?:[A-Z]+\s+)*JOIN|,)\s*((?:\[[^\]]+\]|\"[^\"]+\"|[^\s\.,;()]+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|[^\s\.,;()]+))*)(?=\s+|\s*,|\s*;|$|ON)'这个模式在后面要求遇到空格、逗号(前面可能有空格)、分号(前面可能有空格)、结束或ON。但是,这样写比较复杂。我们可以用:pattern=r'\b(?:FROM|(?:[A-Z]+\s+)*JOIN|,)\s*((?:\[[^\]]+\]|\"[^\"]+\"|[^\s\.,;()]+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|[^\s\.,;()]+))*)\s*(?=[,;]|\s+ON\b|\s+WHERE\b|\s+GROUP\b|\s+HAVING\b|\s+ORDER\b|\s+LIMIT\b|$)'这样,我们使用了一个更复杂的先行断言,确保后面是逗号、分号、ON等关键字或结束。但是,这样写关键字列很长,而且不同数据库的关键字可能不同。因此,我们回到简单的思路:匹配部分(由我们定义的标识符组成),然后交给后续处理去重和清理。我们使用最初的正则达式,然后从匹配结果中去掉可能包含的别(通过空格分割,取第一个部分?不行,因为可能有空格(如果有引号或方括号))。所以,我们坚持使用:pattern=r'\b(?:FROM|(?:[A-Z]+\s+)*JOIN|,)\s*((?:\[[^\]]+\]|\"[^\"]+\"|[^\s\.,;()]+)(?:\s*\.\s*(?:\[[^\]]+\]|\"[^\"]+\"|[^\s\.,;()]+))*)'然后,我们提取后,再对每个匹配的进行处理:如果中有AS关键字,那么我们需要去掉AS后面的部分。但这样很麻烦。因此,我们暂时不处理别,假设用户提供的SQL语句中后面没有紧跟别(或者即使有别,我们提取的部分也是正确的,因为别不在我们的捕获组中)。我们编写代码并测试。测试用例:sql1="SELECT*FROMConfig"->['Config']sql2="SELECT*FROM[dbo].IMEIUser"->['[dbo].IMEIUser']sql8="SELECT*FROMtable1,table2"->['table1','table2']sql9="SELECT*FROMtable1INNERJOINtable2ONtable1.id=table2.id"->['table1','table2']另外,测试有别的情况:sql11="SELECT*FROMtable1ASt1"->应该返回['table1'],因为别不在捕获组中。sql12="SELECT*FROMtable1t1"->同样返回['table1']但是,注意:如果和别之间没有空格?例如:FROMtable1t1,那么table1t1会被当作。但这种情况很少,因为和别通常有空格。所以,我们按照上述实现。代码:</think>### 使用 Python 正则达式提取 SQL 以下是使用 Python 正则达式从 SQL 查询中提取的完整解决方案: ```python import re def extract_table_names(sql): """ 从 SQL 查询中提取(支持带/不带方括号的格式) 参数: sql (str): SQL 查询字符串 返回: list: 提取到的 """ # 核心正则达式(忽略大小写) pattern = r""" (?: # 非捕获组:匹配关键字 FROM\s+ # FROM 关键字 | JOIN\s+ # JOIN 关键字 | ,\s* # 逗号分隔的 ) (?: # 非捕获组:前缀(数据库/架构) (?: \[?\w+\]? # 带或不带方括号的标识符 \s*\.\s* # 点分隔符 )* # 0个或多个前缀 ) ( # 捕获组1:主体 (?: \[[^\]]+\] # 带方括号的 | [^\s\.,;()]+ # 不带方括号的(排除特殊字符) ) ) (?=\s|,|;|$) # 正向先行断言:确保后是空格/逗号/结束符 """ matches = re.findall(pattern, sql, re.VERBOSE | re.IGNORECASE) return [re.sub(r'^\[|\]$', '', name) for name in matches] # 去除方括号 # 测试用例 test_sqls = [ "SELECT * FROM Config", "SELECT * FROM [dbo].IMEIUser", "SELECT * FROM dbo.LotteryLog", "SELECT * FROM [GreenPrize]", "SELECT * FROM [dbo].[Config]", "SELECT * FROM dbo.[Prize]", "SELECT * FROM [DBName].[dbo].[Config]", "SELECT * FROM table1, table2", "SELECT * FROM table1 INNER JOIN table2 ON table1.id=table2.id", "SELECT sID, dtime FROM src JOIN dt ON src.sID=dt.dID" ] for i, sql in enumerate(test_sqls, 1): tables = extract_table_names(sql) print(f"SQL{i}: {sql}\n → {tables}\n") ``` #### 输出结果示例 ``` SQL1: SELECT * FROM Config → ['Config'] SQL2: SELECT * FROM [dbo].IMEIUser → ['IMEIUser'] SQL3: SELECT * FROM dbo.LotteryLog → ['LotteryLog'] SQL4: SELECT * FROM [GreenPrize] → ['GreenPrize'] SQL5: SELECT * FROM [dbo].[Config] → ['Config'] SQL9: SELECT * FROM table1 INNER JOIN table2 ON table1.id=table2.id → ['table1', 'table2'] SQL10: SELECT sID, dtime FROM src JOIN dt ON src.sID=dt.dID → ['src', 'dt'] ``` ### 正则达式关键点解析 1. **多关键字匹配**: - 匹配 `FROM`、`JOIN` 和逗号分隔符 `,` - 使用 `(?:FROM\s+|JOIN\s+|,\s*)` 结构实现 2. **格式处理**: - 带方括号的:`\[[^\]]+\]`(如 `[dbo.Table]`) - 不带方括号的:`[^\s\.,;()]+`(排除特殊字符) - 前缀处理:`(?:\w+\s*\.\s*)*` 支持 `db.schema.table` 格式 3. **边界控制**: - 使用正向先行断言 `(?=\s|,|;|$)` 确保后是有效分隔符 - 避免匹配子查询(如 `FROM (SELECT ...)`) 4. **结果清洗**: - `re.sub(r'^\[|\]$', '', name)` 去除捕获的方括号 ### 应用场景与注意事项 1. **适用场景**: - SQL 日志分析 - 数据库血缘分析 - 查询优化审计 - 动态 SQL 生成 2. **已知限制**: - 不支持子查询中的提取(如 `FROM (SELECT ...) temp`) - 可能误提取字符串常量中的相似文本(如 `WHERE note = 'FROM table'`) - 复杂嵌套查询建议使用 SQL 解析器(如 `sqlparse` 或 `sqlglot`) > 对于生产环境的关键任务,建议结合 SQL 解析库如 [`sqlparse`](https://pypi.org/project/sqlparse/) 或 [`sqlglot`](https://github.com/tobymao/sqlglot) 获得更准确的结果[^2][^3]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值