Как заменить полные имена на их псевдонимы «первые буквы» в стандартном выводе SQL-запроса MS Access, который не имеет псевдонимов?

У меня много запросов из нескольких баз данных MS Access. Я зачитываю их и разделяю на SQL-блоки по стандартным ключевым словам SQL:

Таким образом, чтобы продолжить ответ на этот вопрос, вам необходимо поместить объекты из баз данных MS Access в файл Excel того же формата. Вы можете сказать, что это слишком подробно, и да, это так, но без этого вы вообще не получите отделение FROM-блока от остальной части запроса, которое вообще необходимо для ответа на вопрос.

Столбцы этого входного файла Excel:

Или вообще:

ID Datenbank Имя объекта LastUpdated Objekttyp Objektart SourceTableName Abfrage_SQL Fehler Select Into From Where Group_By Имея Order_By New SQL Codes Сопоставление

Основной SQL-запрос находится в столбце H, а разделение на блоки варьируется от столбцов J до P. В коде вам понадобятся столбец H и столбец L в качестве входных данных.

Вы можете получить ответ без этого входного файла Excel и с другим кодом, но вам не обойтись без разделения кода с помощью некоторого регулярного выражения, и я не хотел изобретать велосипед, поэтому проверьте ссылку, как туда добраться. Запросы из имеющихся баз данных MS Access не имеют псевдонимов.

Имейте в виду, что запросы в ваших базах данных MS Access могут иметь псевдонимы. Если они у вас есть постоянно, вам не нужен этот вопрос. Но если они бывают лишь иногда, нужно изменить код ответа.

Задача

Я не хочу ставить псевдонимы с помощью Regex или даже вручную и в несколько этапов. Вместо этого я хочу запустить на нем код Python, который сделает все это за один раз.

Мне нужно заменить полные имена таблиц и представлений их стандартизированными псевдонимами. Псевдоним должен быть составлен из первых букв каждого имени, разделенных знаком «_», чтобы «my_random_table» превратилась в «mrt». Если сокращению присвоено более одного полного имени, повторяющемуся сокращению должен быть присвоен возрастающий номер.

Полный запрос от MS Access может выглядеть так:

select my_random_table.* from my_random_table

Это должно быть сокращено с помощью таких псевдонимов:

select mrt.* from my_random_table mrt

Входной файл Excel содержит список из более чем 100 запросов в столбце H и их FROM-блок в столбце L.

Как заменить полные имена на их псевдонимы «первые буквы» в стандартном выводе SQL-запроса MS Access, который не имеет псевдонимов? Это должно быть сделано с помощью кода Python, который запускается во входном файле Excel. Этот входной файл Excel можно создать с помощью ссылок, перечисленных выше.

ПС

Теги "excel" и "ms-access" не суть вопроса, они даже не нужны. Ответ может помочь и в любых других настройках SQL. Я поясняю это, поскольку ответ использует выходные данные MS Access и MS Excel, но вы можете обойти это, переписав код для другой настройки программного обеспечения.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Код Python для получения двух новых столбцов (New SQL Codes и Mapping) в конце входного файла Excel и вывода их в качестве нового выходного файла:

import pandas as pd
import re

# Define SQL keywords
SQL_KEYWORDS = {
    'SELECT', 'FROM', 'WHERE', 'JOIN', 'INNER', 'OUTER', 'LEFT', 'RIGHT', 
    'ON', 'AND', 'OR', 'GROUP', 'BY', 'ORDER', 'DESC', 'ASC', 'HAVING', 
    'INSERT', 'UPDATE', 'DELETE', 'VALUES', 'SET', 'CREATE', 'DROP', 
    'ALTER', 'TABLE', 'VIEW', 'AS', 'DISTINCT', 'LIKE', 'IN', 'BETWEEN', 
    'EXISTS', 'NULL', 'IS', 'CASE', 'WHEN', 'THEN', 'ELSE', 'END', 
    'UNION', 'ALL', 'LIMIT', 'OFFSET', 'SELECTED', 'EXPLAIN', 'WITH', 
    'INTO'
}

def is_valid_object_name(full_name):
    """Checks if the object name is a table or view."""
    return full_name.upper() not in SQL_KEYWORDS

def generate_short_name(full_name, existing_aliases):
    """Generates a short name from the full table name and ensures it is unique."""
    parts = full_name.split('_')
    short_name = ''.join(part[0] for part in parts if part)  # Generate abbreviation
    short_name = short_name.lower()

    # Ensure uniqueness
    if short_name in existing_aliases:
        count = 2
        new_short_name = f"{short_name}{count}"
        while new_short_name in existing_aliases:
            count += 1
            new_short_name = f"{short_name}{count}"
        short_name = new_short_name

    existing_aliases.add(short_name)
    return short_name

def process_queries(queries, from_clauses):
    """Processes the SQL queries and replaces the table names with aliases."""
    all_output_queries = []
    all_short_names = []

    first_query_processed = False  # Flag to track the first query

    for query, from_clause in zip(queries, from_clauses):
        if query and not first_query_processed:  # Debugging only for the first filled query
            print("\n=== Debugging Output for the First Query == = ")
            print("Original Query:", query)
            print("FROM Clause:", from_clause)

        short_names = {}
        existing_aliases = set()  # Collection for already used aliases
        output_query = query.strip()

        # Regex to identify table names and views (refined)
        found_tables = re.findall(r'\b(?!(INNER|JOIN|ON|AND|OR|WHERE|GROUP|ORDER|ASC|DESC)\b)(\w+(?:_\w+)?)\b', from_clause)
        found_tables = [name[1] for name in found_tables]  # Only extract actual table names
        
        if not first_query_processed:  # Debug information only for the first query
            print("Found Tables:", found_tables)  # Output of found tables

        for full_name in found_tables:
            if is_valid_object_name(full_name) and full_name not in short_names:
                short_name = generate_short_name(full_name, existing_aliases)
                short_names[full_name] = short_name

        if not first_query_processed:  # Debug information only for the first query
            print("Generated Aliases:", short_names)  # Output of generated aliases

        # Inserting aliases in the FROM block
        for full_name, short_name in short_names.items():
            if re.search(r'\b' + re.escape(full_name) + r'\s+' + re.escape(short_name) + r'\b', from_clause) is None:
                from_clause = re.sub(
                    r'\b' + re.escape(full_name) + r'\b',
                    f'{full_name} {short_name}',  # Full name and alias
                    from_clause, count=1
                )
                if not first_query_processed:  # Debug information only for the first query
                    print(f"Inserted: {full_name} with Alias {short_name}")

        if query:
            # Replacing table names with aliases in the SELECT query
            for full_name, short_name in short_names.items():
                output_query = re.sub(r'\b' + re.escape(full_name) + r'\b', short_name, output_query)

            if not first_query_processed:  # Debug information only for the first query
                print("Query after Alias Replacements:", output_query)

            # Inserting the FROM block into the complete query
            output_query = re.sub(r'\bFROM\b.*', f'FROM {from_clause}', output_query, count=1)

            if not first_query_processed:  # Debug information only for the first query
                print("Query after Adding the FROM Clause:", output_query)

            # Column names prefixed with table names should not be changed
            for full_name, short_name in short_names.items():
                output_query = re.sub(r'\b' + re.escape(full_name) + r'\.', f'{short_name}.', output_query)

            if not first_query_processed:
                print("Processed Query:", output_query)
                first_query_processed = True  # Set the flag after processing the first query

            all_output_queries.append(output_query)  # Add the processed query
            all_short_names.append(short_names)       # Add the abbreviations

    return all_output_queries, all_short_names

# Read the Excel file
input_file = 'input_file.xlsx'  # Path to the input file
output_file = 'output_file.xlsx'  # Path to the output file

# Load the Excel file and read relevant data
df = pd.read_excel(input_file)

# Extract SQL queries from column H (index 7) and the FROM clause from column L (index 11)
raw_queries = df.iloc[:, 7].dropna().tolist()  # Column H
from_clauses = df.iloc[:, 11].dropna().tolist()  # Column L

# Process queries
processed_queries, abbreviations = process_queries(raw_queries, from_clauses)

# Add new SQL codes and mappings to DataFrame
df['New SQL Codes'] = ''
df['Mapping'] = ''

processed_index = 0  # Index for processed queries
for i in range(len(df)):  # Loop through original queries
    query = df.at[i, 'Abfrage_SQL']
    if isinstance(query, str) and query:  # Check
        df.at[i, 'New SQL Codes'] = processed_queries[processed_index]

        # Create mapping only for table names, excluding column names
        mapping_str = ', '.join(f"{k}: {v}" for k, v in abbreviations[processed_index].items()
                                 if is_valid_object_name(k) and not '.' in k)  # Only use valid table names

        df.at[i, 'Mapping'] = mapping_str.strip()  # Prepare mapping

        processed_index += 1  # Only increment for processed queries

# Save the new data to an Excel file
with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
    df.to_excel(writer, index=False, sheet_name='SQL_Queries')

print(f'Data has been saved in {output_file}.')

В столбце New SQL Codes перечислены все новые «псевдонимы» SQL-запросы. В столбце Mapping перечислены все таблицы и представления с их псевдонимами, поэтому:

SELECT my_random_table.*, my_raw_tbl.* 
FROM my_random_table 
INNER JOIN my_raw_tbl 
ON my_random_table.id = my_raw_tbl.random_tbl_id

..становится:

SELECT mrt.*, mrt2.* 
FROM my_random_table mrt 
INNER JOIN my_raw_tbl mrt2 
ON mrt.id = mrt2.random_tbl_id

Другие вопросы по теме