sqlServerConnector 0.1.8__tar.gz → 0.1.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/PKG-INFO +4 -2
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/README.md +3 -1
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/pyproject.toml +1 -1
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/connector.py +29 -49
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/PKG-INFO +4 -2
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/setup.cfg +0 -0
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/__init__.py +0 -0
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/SOURCES.txt +0 -0
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/dependency_links.txt +0 -0
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/requires.txt +0 -0
- {sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlServerConnector
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: A custom SQL Server Connector for ETL processes with Pandas
|
|
5
5
|
Author-email: Nguyen Minh Son <nguyen.minhson1511@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/johnnyb1509/sqlServerConnector
|
|
@@ -20,8 +20,10 @@ Requires-Dist: jupyterlab
|
|
|
20
20
|
# SQL Server Connector
|
|
21
21
|
|
|
22
22
|
Thư viện kết nối SQL Server chuyên dụng cho các tác vụ ETL, được tối ưu hóa cho **Pandas**, hỗ trợ **Tiếng Việt (Unicode)** và **Upsert (Merge)** hiệu năng cao.
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
### **Update 0.1.9**
|
|
24
25
|
> Sửa lỗi nhỏ liên quan đến việc upsert với các bảng có cột chứa Tiếng Việt
|
|
26
|
+
> Thay vì dùng bảng tạm (##Staging), sẽ dùng bảng vật lý tạm thời (Physical Staging Table) có tên chứa UUID (để đảm bảo duy nhất, không trùng lặp giữa các luồng chạy). Sau khi Upsert xong, ta sẽ DROP bảng này ngay lập tức. Cách này tương thích 100% với Pandas và SQLAlchemy.
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
## 🚀 Tính năng nổi bật
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# SQL Server Connector
|
|
2
2
|
|
|
3
3
|
Thư viện kết nối SQL Server chuyên dụng cho các tác vụ ETL, được tối ưu hóa cho **Pandas**, hỗ trợ **Tiếng Việt (Unicode)** và **Upsert (Merge)** hiệu năng cao.
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
### **Update 0.1.9**
|
|
5
6
|
> Sửa lỗi nhỏ liên quan đến việc upsert với các bảng có cột chứa Tiếng Việt
|
|
7
|
+
> Thay vì dùng bảng tạm (##Staging), sẽ dùng bảng vật lý tạm thời (Physical Staging Table) có tên chứa UUID (để đảm bảo duy nhất, không trùng lặp giữa các luồng chạy). Sau khi Upsert xong, ta sẽ DROP bảng này ngay lập tức. Cách này tương thích 100% với Pandas và SQLAlchemy.
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
## 🚀 Tính năng nổi bật
|
|
@@ -119,18 +119,7 @@ class SQLServerConnector:
|
|
|
119
119
|
conflict_strategy: Literal['last', 'skip'] = 'last',
|
|
120
120
|
auto_evolve_schema: bool = False):
|
|
121
121
|
"""
|
|
122
|
-
Hàm Upsert
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
df: DataFrame cần upload.
|
|
126
|
-
target_table: Tên bảng đích.
|
|
127
|
-
match_columns: Danh sách cột dùng làm Key so khớp (Primary Key).
|
|
128
|
-
conflict_strategy:
|
|
129
|
-
- 'last': Update ghi đè dữ liệu mới vào dòng cũ (Default).
|
|
130
|
-
- 'skip': Nếu trùng key thì bỏ qua, không update.
|
|
131
|
-
auto_evolve_schema:
|
|
132
|
-
- True: Tự động thêm cột vào DB nếu DF có cột mới.
|
|
133
|
-
- False: Bỏ qua các cột trong DF mà DB không có (Strict Schema).
|
|
122
|
+
Hàm Upsert sửa lỗi 'Table not found'.
|
|
134
123
|
"""
|
|
135
124
|
if df.empty:
|
|
136
125
|
logger.warning(f"DataFrame for {target_table} is empty. Skip.")
|
|
@@ -139,17 +128,19 @@ class SQLServerConnector:
|
|
|
139
128
|
# 1. Map Unicode Types
|
|
140
129
|
dtype_mapping = self._generate_dtype_mapping(df)
|
|
141
130
|
|
|
142
|
-
#
|
|
143
|
-
|
|
131
|
+
# [FIX] Dùng tên bảng thường (Staging_UUID) thay vì ##Staging
|
|
132
|
+
# Lý do: Pandas to_sql gặp lỗi khi check sự tồn tại của bảng ## trong transaction.
|
|
133
|
+
# Bảng này sẽ được drop thủ công ở khối finally.
|
|
134
|
+
staging_table = f"Staging_{uuid.uuid4().hex[:8]}"
|
|
144
135
|
|
|
145
136
|
try:
|
|
146
137
|
with self.engine.begin() as conn:
|
|
147
|
-
# --- A. Kiểm tra
|
|
138
|
+
# --- A. Kiểm tra & Tạo bảng đích ---
|
|
148
139
|
inspector = inspect(conn)
|
|
149
140
|
if not inspector.has_table(target_table):
|
|
150
141
|
logger.info(f"Table {target_table} not found. Creating new...")
|
|
151
142
|
df.to_sql(target_table, conn, index=False, dtype=dtype_mapping)
|
|
152
|
-
|
|
143
|
+
|
|
153
144
|
if match_columns:
|
|
154
145
|
pk_str = ", ".join([f"[{c}]" for c in match_columns])
|
|
155
146
|
try:
|
|
@@ -158,25 +149,22 @@ class SQLServerConnector:
|
|
|
158
149
|
logger.warning(f"Could not create PK: {e}")
|
|
159
150
|
return
|
|
160
151
|
|
|
161
|
-
# --- B.
|
|
152
|
+
# --- B. Schema Evolution ---
|
|
162
153
|
db_cols = self._get_table_columns(target_table, conn)
|
|
163
154
|
df_cols = list(df.columns)
|
|
164
|
-
|
|
165
|
-
# Tìm cột có trong DF mà không có trong DB
|
|
166
155
|
new_cols = [c for c in df_cols if c not in db_cols]
|
|
167
156
|
|
|
168
157
|
if new_cols:
|
|
169
158
|
if auto_evolve_schema:
|
|
170
159
|
self._add_missing_columns(target_table, new_cols, dtype_mapping, conn)
|
|
171
|
-
db_cols.extend(new_cols)
|
|
160
|
+
db_cols.extend(new_cols)
|
|
172
161
|
else:
|
|
173
|
-
# Nếu không auto evolve, chỉ giữ lại các cột khớp với DB
|
|
174
162
|
valid_cols = [c for c in df_cols if c in db_cols]
|
|
175
163
|
if len(valid_cols) < len(df_cols):
|
|
176
|
-
logger.warning(f"Schema strict: Dropping columns {new_cols} because they are not in DB.")
|
|
177
164
|
df = df[valid_cols]
|
|
178
|
-
|
|
179
|
-
# --- C. Đẩy vào Staging
|
|
165
|
+
|
|
166
|
+
# --- C. Đẩy vào Staging ---
|
|
167
|
+
# to_sql hoạt động tốt với bảng thường
|
|
180
168
|
df.to_sql(
|
|
181
169
|
name=staging_table,
|
|
182
170
|
con=conn,
|
|
@@ -185,60 +173,52 @@ class SQLServerConnector:
|
|
|
185
173
|
dtype=dtype_mapping
|
|
186
174
|
)
|
|
187
175
|
|
|
188
|
-
# --- D.
|
|
189
|
-
# Chỉ lấy các cột chung giữa DF và DB để Merge (tránh lỗi cột không tồn tại)
|
|
176
|
+
# --- D. MERGE ---
|
|
190
177
|
common_cols = [c for c in df.columns if c in db_cols]
|
|
191
|
-
|
|
192
178
|
on_clause = " AND ".join([f"Target.[{col}] = Source.[{col}]" for col in match_columns])
|
|
193
179
|
|
|
194
|
-
# Logic Insert
|
|
195
180
|
insert_cols = ", ".join([f"[{col}]" for col in common_cols])
|
|
196
181
|
insert_vals = ", ".join([f"Source.[{col}]" for col in common_cols])
|
|
197
182
|
|
|
198
|
-
# Logic Update
|
|
199
183
|
merge_sql = ""
|
|
200
|
-
|
|
201
|
-
# Trường hợp 1: Update ('last')
|
|
202
184
|
if conflict_strategy == 'last':
|
|
203
185
|
update_cols = [c for c in common_cols if c not in match_columns]
|
|
204
186
|
if update_cols:
|
|
205
187
|
update_set = ", ".join([f"Target.[{col}] = Source.[{col}]" for col in update_cols])
|
|
206
188
|
merge_sql = f"""
|
|
207
|
-
MERGE [{target_table}] AS Target
|
|
208
|
-
USING {staging_table} AS Source
|
|
189
|
+
MERGE [{target_table}] AS Target USING [{staging_table}] AS Source
|
|
209
190
|
ON {on_clause}
|
|
210
|
-
WHEN MATCHED THEN
|
|
211
|
-
|
|
212
|
-
WHEN NOT MATCHED BY TARGET THEN
|
|
213
|
-
INSERT ({insert_cols}) VALUES ({insert_vals});
|
|
191
|
+
WHEN MATCHED THEN UPDATE SET {update_set}
|
|
192
|
+
WHEN NOT MATCHED BY TARGET THEN INSERT ({insert_cols}) VALUES ({insert_vals});
|
|
214
193
|
"""
|
|
215
194
|
else:
|
|
216
|
-
# Nếu chỉ có cột PK, không có gì để update -> Chỉ Insert if not exists
|
|
217
195
|
merge_sql = f"""
|
|
218
|
-
MERGE [{target_table}] AS Target
|
|
219
|
-
USING {staging_table} AS Source
|
|
196
|
+
MERGE [{target_table}] AS Target USING [{staging_table}] AS Source
|
|
220
197
|
ON {on_clause}
|
|
221
|
-
WHEN NOT MATCHED BY TARGET THEN
|
|
222
|
-
INSERT ({insert_cols}) VALUES ({insert_vals});
|
|
198
|
+
WHEN NOT MATCHED BY TARGET THEN INSERT ({insert_cols}) VALUES ({insert_vals});
|
|
223
199
|
"""
|
|
224
200
|
|
|
225
|
-
# Trường hợp 2: Skip (Chỉ Insert, không Update)
|
|
226
201
|
elif conflict_strategy == 'skip':
|
|
227
202
|
merge_sql = f"""
|
|
228
|
-
MERGE [{target_table}] AS Target
|
|
229
|
-
USING {staging_table} AS Source
|
|
203
|
+
MERGE [{target_table}] AS Target USING [{staging_table}] AS Source
|
|
230
204
|
ON {on_clause}
|
|
231
|
-
WHEN NOT MATCHED BY TARGET THEN
|
|
232
|
-
INSERT ({insert_cols}) VALUES ({insert_vals});
|
|
205
|
+
WHEN NOT MATCHED BY TARGET THEN INSERT ({insert_cols}) VALUES ({insert_vals});
|
|
233
206
|
"""
|
|
234
207
|
|
|
235
208
|
conn.execute(text(merge_sql))
|
|
236
|
-
|
|
237
|
-
logger.info(f"Upserted {len(df)} rows to {target_table} (Strategy: {conflict_strategy})")
|
|
209
|
+
logger.info(f"Upserted {len(df)} rows to {target_table}")
|
|
238
210
|
|
|
239
211
|
except Exception as e:
|
|
240
212
|
logger.error(f"Upsert failed for {target_table}: {e}")
|
|
241
213
|
raise e
|
|
214
|
+
finally:
|
|
215
|
+
# [QUAN TRỌNG] Luôn luôn xóa bảng Staging rác dù thành công hay thất bại
|
|
216
|
+
# Sử dụng kết nối mới để đảm bảo lệnh Drop được thực thi
|
|
217
|
+
try:
|
|
218
|
+
with self.engine.begin() as conn:
|
|
219
|
+
conn.execute(text(f"IF OBJECT_ID('{staging_table}', 'U') IS NOT NULL DROP TABLE [{staging_table}]"))
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.warning(f"Could not clean up staging table {staging_table}: {e}")
|
|
242
222
|
|
|
243
223
|
def dispose(self):
|
|
244
224
|
self.engine.dispose()
|
{sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlServerConnector
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: A custom SQL Server Connector for ETL processes with Pandas
|
|
5
5
|
Author-email: Nguyen Minh Son <nguyen.minhson1511@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/johnnyb1509/sqlServerConnector
|
|
@@ -20,8 +20,10 @@ Requires-Dist: jupyterlab
|
|
|
20
20
|
# SQL Server Connector
|
|
21
21
|
|
|
22
22
|
Thư viện kết nối SQL Server chuyên dụng cho các tác vụ ETL, được tối ưu hóa cho **Pandas**, hỗ trợ **Tiếng Việt (Unicode)** và **Upsert (Merge)** hiệu năng cao.
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
### **Update 0.1.9**
|
|
24
25
|
> Sửa lỗi nhỏ liên quan đến việc upsert với các bảng có cột chứa Tiếng Việt
|
|
26
|
+
> Thay vì dùng bảng tạm (##Staging), sẽ dùng bảng vật lý tạm thời (Physical Staging Table) có tên chứa UUID (để đảm bảo duy nhất, không trùng lặp giữa các luồng chạy). Sau khi Upsert xong, ta sẽ DROP bảng này ngay lập tức. Cách này tương thích 100% với Pandas và SQLAlchemy.
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
## 🚀 Tính năng nổi bật
|
|
File without changes
|
|
File without changes
|
{sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/requires.txt
RENAMED
|
File without changes
|
{sqlserverconnector-0.1.8 → sqlserverconnector-0.1.9}/src/sqlServerConnector.egg-info/top_level.txt
RENAMED
|
File without changes
|