Как использовать регулярное выражение для извлечения текста по порядку? - PullRequest
6 голосов
/ 26 мая 2020

Я работаю над сценарием потока python. Что в основном анализирует SQL операторов и получает целевую таблицу и исходные таблицы. Пример

CREATE TABLE TAR_TAB1 AS
SELECT * FROM SRC_TAB1 JOIN SRC_TAB2

CREATE TABLE TAR_TAB2 AS 
SELECT * FROM SRC_TAB3 JOIN SRC_TAB4

INSERT INTO TAR_TAB3 AS
SELECT * FROM SRC_TAB5 JOIN SRC_TAB6

Я написал регулярное выражение

target = re.findall(r'\w+(?=\s+AS)', data)
source = re.findall(r'(?:FROM|JOIN)\s*([^\s]+), data)

результаты такие, как ожидалось,

target list contains, TAR_TAB1, TAR_TAB2, TAR_TAB3
source list contains, SRC_TAB1, SRC_TAB2, SRC_TAB3, SRC_TAB4, ETC

КАК ПОЛУЧИТЬ ВЫВОД, КАК ЛИБО СПИСОК ИЛИ КАДР ДАННЫХ, предпочтительно dataframe, как таковой поток извлечения не нарушается.

target         source
TAR_TAB1       SRC_TAB1
TAR_TAB1       SRC_TAB2
TAR_TAB2       SRC_TAB3
TAR_TAB2       SRC_TAB4
TAR_TAB3       SRC_TAB5
TAR_TAB4       SRC_TAB6

Ответы [ 2 ]

0 голосов
/ 18 июня 2020

Этот подход использует pyparsing для фактического анализа операторов SQL (в рамках синтаксиса подмножества, который вы показали в своих примерах):

import pyparsing as pp
ppc = pp.pyparsing_common


ident = ppc.identifier

CREATE, INSERT, INTO, TABLE, AS, SELECT, FROM, JOIN = \
    map(pp.CaselessKeyword, "CREATE INSERT INTO TABLE AS SELECT FROM JOIN".split())

select_stmt = (SELECT
               + (pp.delimitedList(ident) | '*')("columns")
               + FROM
               + ((ident + JOIN.suppress() + ident) | ident)("tables"))

src_target_stmt = ((CREATE + TABLE | INSERT + INTO)("action")
                   + ident("target")
                   + AS
                   + pp.Group(select_stmt)("source"))


tests = """
    CREATE TABLE TAR_TAB1 AS SELECT * FROM SRC_TAB1 JOIN SRC_TAB2
    CREATE TABLE TAR_TAB2 AS SELECT * FROM SRC_TAB3 JOIN SRC_TAB4
    INSERT INTO TAR_TAB3 AS SELECT COL1,COL2 FROM SRC_TAB5 JOIN SRC_TAB6
    """

# useful for debugging
#src_target_stmt.runTests(tests)

# dump parsed values out as CSV output
for t in tests.splitlines():
    if not t.strip():
        continue
    result = src_target_stmt.parseString(t)
    target = result.target
    action = result.action[0]
    for src in result.source.tables:
        print("{},{},{}".format(action, target, src))

Выводит:

CREATE,TAR_TAB1,SRC_TAB1
CREATE,TAR_TAB1,SRC_TAB2
CREATE,TAR_TAB2,SRC_TAB3
CREATE,TAR_TAB2,SRC_TAB4
INSERT,TAR_TAB3,SRC_TAB5
INSERT,TAR_TAB3,SRC_TAB6

Как вы продолжаете свой проект и обнаруживаете новые требования (необходимо извлечь действие SQL, как показано в этом примере, или вы найдете другие варианты конструкций SQL, которые вам нужно понять при синтаксическом анализе), расширение синтаксического анализатора будет проще и более удобен в обслуживании, чем расширение регулярного выражения.

0 голосов
/ 27 мая 2020

Вот решение:

targets = re.findall(r'(?:CREATE\s+TABLE|INSERT\s+INTO)\s+([a-z0-9A-Z_]+)\s+AS', text)
sources = re.findall(r'SELECT\s+\*\s+FROM\s([a-z0-9A-Z_]+)\s+JOIN\s+([a-z0-9A-Z_]+)', text)

# each target has multiple sources, so repeat each target n times per 
# number of sources.
lens = [len(src) for src in sources]
targets = np.repeat(targets, lens) 

# 'flatten' the list of sources from [(t1, t2), (t3, t4)] to 
# [t1, t2, t3, t4]
sources = [tab for exp in sources for tab in exp]

pd.DataFrame({"src": sources, "tgt": targets})

Результаты:

        src       tgt
0  SRC_TAB1  TAR_TAB1
1  SRC_TAB2  TAR_TAB1
2  SRC_TAB3  TAR_TAB2
3  SRC_TAB4  TAR_TAB2
4  SRC_TAB5  TAR_TAB3
5  SRC_TAB6  TAR_TAB3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...