table_to_csv.py
import cx_Oracle
import csv
import os
from datetime import datetime
cx_Oracle.init_oracle_client(lib_dir=r'D:\oracleclient\instantclient_23_8')
def export_oracle_query_to_csv(sql, output_file=None):
db_config = {
'user': 'system',
'password': 'xxxxx',
'dsn': '10.12.8.55:1521/gemesdb',
'encoding': 'UTF-8'
}
if not output_file:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"table_structrue_{timestamp}.csv"
try:
with cx_Oracle.connect(**db_config) as connection:
with connection.cursor() as cursor:
cursor.execute(sql)
column_names = [desc[0] for desc in cursor.description]
with open(output_file, 'w', newline='', encoding='utf-8-sig') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(column_names)
batch_size = 1000
while True:
rows = cursor.fetchmany(batch_size)
if not rows:
break
writer.writerows(rows)
print(f"数据已成功导出到 {os.path.abspath(output_file)}")
return output_file
except Exception as e:
print(f"导出过程发生错误: {str(e)}")
raise
sql = """
SELECT
tc.owner,
tc.table_name,
'' as tcomments,
cc.column_name,
cols.data_type,
NVL(cols.data_length, 0) as data_length,
NVL(cols.data_precision, 0) as data_precision,
NVL(cols.data_scale, 0) as data_scale,
cols.nullable,
cols.data_default,
'' as ccomments
FROM
all_tab_comments tc
JOIN
all_col_comments cc
ON tc.table_name = cc.table_name
AND tc.owner = cc.owner
JOIN
all_tab_columns cols
ON cc.table_name = cols.table_name
AND cc.column_name = cols.column_name
AND cc.owner = cols.owner
WHERE
tc.owner IN ('MES_PROD','DCS_PROD','DFS_PROD','MES_REPORT_PROD','RMS_PROD','GOERTEK','SF_SECURITY','SF_AMC','SF_QUARTY')
and cc.comments is null
ORDER BY
tc.owner,
tc.table_name,
cols.column_id
"""
export_oracle_query_to_csv(
sql=sql
)
table_to_html.py
import cx_Oracle
import json
import sys
from datetime import datetime
from jinja2 import Environment, FileSystemLoader
import os
cx_Oracle.init_oracle_client(lib_dir=r'D:\oracleclient\instantclient_23_8')
def query_to_html(config, sql_query, template_file, output_file, dbname):
try:
connection = cx_Oracle.connect(
user=config['username'],
password=config['password'],
dsn=config['dsn']
)
print(f"成功连接到数据库: {connection.version}")
cursor = connection.cursor()
cursor.execute(sql_query)
columns = [desc[0] for desc in cursor.description]
users_data = {}
for row in cursor:
row_data = dict(zip(columns, row))
owner = row_data['OWNER']
table_name = row_data['TABLE_NAME']
tcomments = row_data['TCOMMENTS']
column_info = {
"name": row_data['COLUMN_NAME'],
"data_type": row_data['DATA_TYPE'],
"length": row_data['DATA_LENGTH'] if row_data['DATA_LENGTH'] != 0 else None,
"precision": row_data['DATA_PRECISION'] if row_data['DATA_PRECISION'] != 0 else None,
"scale": row_data['DATA_SCALE'] if row_data['DATA_SCALE'] != 0 else None,
"nullable": row_data['NULLABLE'],
"default_value": row_data['DATA_DEFAULT'],
"comment": row_data['CCOMMENTS']
}
if owner not in users_data:
users_data[owner] = {
"username": owner,
"usertable_count": 0,
"tables": {}
}
if table_name not in users_data[owner]['tables']:
users_data[owner]['tables'][table_name] = {
"name": table_name,
"comment": tcomments,
"columns": []
}
users_data[owner]['usertable_count'] += 1
users_data[owner]['tables'][table_name]['columns'].append(column_info)
for user in users_data.values():
user['tables'] = list(user['tables'].values())
render_data = {
"dbname": dbname,
"user_count": len(users_data),
"table_count": sum(user['usertable_count'] for user in users_data.values()),
"generate_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"users": list(users_data.values())
}
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template(template_file)
rendered_html = template.render(render_data)
with open(output_file, 'w', encoding='utf-8') as f:
f.write(rendered_html)
print(f"数据已成功导出到: {output_file}")
except cx_Oracle.Error as error:
print(f"数据库错误: {error}")
except Exception as e:
print(f"发生错误: {e}")
finally:
if 'connection' in locals():
connection.close()
print("数据库连接已关闭")
if __name__ == "__main__":
config = {
"username": "system",
"password": "Zeta@xxxx#$",
"dsn": "10.12.8.61:1521/edadb"
}
sql_query = """
SELECT
tc.owner,
tc.table_name,
tc.comments as tcomments,
cc.column_name,
cols.data_type,
NVL(cols.data_length, 0) as data_length,
NVL(cols.data_precision, 0) as data_precision,
NVL(cols.data_scale, 0) as data_scale,
cols.nullable,
cols.data_default,
replace(replace (replace (cc.comments,',',','),chr(10),'|'),chr(13),'|') as ccomments
FROM
all_tab_comments tc
JOIN
all_col_comments cc
ON tc.table_name = cc.table_name
AND tc.owner = cc.owner
JOIN
all_tab_columns cols
ON cc.table_name = cols.table_name
AND cc.column_name = cols.column_name
AND cc.owner = cols.owner
WHERE
tc.owner IN ('SPC_PROD')
ORDER BY
tc.owner,
tc.table_name,
cols.column_id
"""
template_file = "table_template.html"
output_file = "SPC_PROD_table_structure.html"
query_to_html(config, sql_query, template_file, output_file,dbname="edadb")
table_template.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ dbname }}数据库表结构</title>
<style>
body {
font-family: 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f9f9f9;
margin: 0;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.user-module {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
margin-bottom: 30px;
overflow: hidden;
}
.user-header {
background-color: #4a6fa5;
color: white;
padding: 15px 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.user-header h2 {
margin: 0;
font-size: 1.5em;
}
.user-content {
padding: 20px;
}
.table-section {
margin-bottom: 20px;
border: 1px solid #e8e8e8;
border-radius: 4px;
}
.table-header {
background-color: #f8f8f8;
padding: 12px 15px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
}
.table-title {
display: flex;
align-items: center;
}
.table-name {
font-size: 1.1em;
font-weight: 500;
}
.table-comment {
color: #666;
font-style: italic;
margin-left: 12px;
}
.table-expand-btn {
background-color: #f0f0f0;
border: none;
padding: 6px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 0.9em;
transition: background-color 0.3s;
}
.table-expand-btn:hover {
background-color: #e0e0e0;
}
.field-table {
width: 100%;
border-collapse: collapse;
font-size: 0.95em;
display: none;
}
.field-table th, .field-table td {
padding: 10px 12px;
border: 1px solid #ddd;
text-align: left;
}
.field-table th {
background-color: #f0f0f0;
font-weight: 600;
}
.field-table tr:nth-child(even) {
background-color: #f8f8f8;
}
.nullable-yes {
color: #008000;
}
.nullable-no {
color: #cc0000;
}
</style>
</head>
<body>
<div class="container">
<h1 class="text-center mb-4">{{ dbname }}数据库表结构 用户数量:{{ user_count }}表数量: {{ table_count }}</h1>
<p class="text-center text-gray-600 mb-6">生成时间: {{ generate_time }}</p>
{% for user in users %}
<div class="user-module">
<div class="user-header" onclick="toggleUserContent('{{ user.username }}')">
<h2>用户: {{ user.username }} 表数量:{{ user.usertable_count }}</h2>
<button class="expand-btn" id="{{ user.username }}_btn">展开</button>
</div>
<div class="user-content" id="{{ user.username }}_content">
{% for table in user.tables %}
<div class="table-section">
<div class="table-header" onclick="toggleTableContent('{{ user.username }}', '{{ table.name }}')">
<div class="table-title">
<span class="table-name">{{ table.name }}</span>
{% if table.comment %}
<span class="table-comment">---- {{ table.comment }}</span>
{% endif %}
</div>
<button class="table-expand-btn" id="{{ user.username }}_{{ table.name }}_btn">展开</button>
</div>
<table class="field-table" id="{{ user.username }}_{{ table.name }}_content">
<thead>
<tr>
<th>字段名</th>
<th>数据类型</th>
<th>长度</th>
<th>精度</th>
<th>刻度</th>
<th>是否可为空</th>
<th>默认值</th>
<th>字段注释</th>
</tr>
</thead>
<tbody>
{% for column in table.columns %}
<tr>
<td>{{ column.name }}</td>
<td>{{ column.data_type }}</td>
<td>{{ column.length|default('-') }}</td>
<td>{{ column.precision|default('-') }}</td>
<td>{{ column.scale|default('-') }}</td>
<td class="{{ 'nullable-yes' if column.nullable == 'Y' else 'nullable-no' }}">
{{ '是' if column.nullable == 'Y' else '否' }}
</td>
<td>{{ column.default_value|default('-') }}</td>
<td>{{ column.comment|default('-') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
<script>
function toggleUserContent(username) {
const content = document.getElementById(`${username}_content`);
const btn = document.getElementById(`${username}_btn`);
if (content.style.display === 'none') {
content.style.display = 'block';
btn.textContent = '收起';
} else {
content.style.display = 'none';
btn.textContent = '展开';
}
}
function toggleTableContent(username, tableName) {
const content = document.getElementById(`${username}_${tableName}_content`);
const btn = document.getElementById(`${username}_${tableName}_btn`);
if (content.style.display === 'none') {
content.style.display = 'table';
btn.textContent = '收起';
} else {
content.style.display = 'none';
btn.textContent = '展开';
}
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.user-content').forEach(content => {
content.style.display = 'none';
});
document.querySelectorAll('.field-table').forEach(table => {
table.style.display = 'none';
});
document.querySelectorAll('.table-expand-btn').forEach(btn => {
btn.textContent = '展开';
});
});
</script>
</div>
</body>
</html>