mssql-python 드라이버를 사용해 SQL Server, Azure SQL Database, Azure SQL Managed Instance, SQL 데이터베이스에 연결할 때 흔히 발생하는 문제를 진단하고 해결Microsoft Fabric.
설치 문제
PIP 설치 실패 또는 소스에서 빌드
증상:
error: Microsoft Visual C++ 14.0 or greater is required
ERROR: Failed building wheel for mssql-python
가능한 원인 및 해결 방법:
사용 중인 플랫폼용 사전 빌드된 wheel이 없습니다
- 지원되는 Python 버전(3.10 이상)과 플랫폼을 사용하고 있는지 확인하세요. 호환성 매트릭스는 지원 수명 주기 를 참조하세요.
pip install --upgrade pip로 설치하기 전에 pip를 업그레이드하세요. 반복 가능한 팀 환경에서는 반복 가능한 배포 에서 잠긴 워크플로우나 컨테이너 및 로컬 개발 의 컨테이너 패턴을 사용하여 로컬 머신 드리프트를 줄이세요.
- 지원되는 Python 버전(3.10 이상)과 플랫폼을 사용하고 있는지 확인하세요. 호환성 매트릭스는 지원 수명 주기 를 참조하세요.
가상 환경이 활성화되지 않음
- 먼저 가상 환경을 활성화하세요. 시스템에 Python을 설치하면 권한 오류나 충돌이 발생할 수 있습니다.
python -m venv .venv .venv\Scripts\activate pip install mssql-python
-
누락된 리눅스 시스템 라이브러리
- 이 드라이버는 리눅스에서 소수의 시스템 라이브러리를 필요로 합니다. 설치해야 할 패키지는 플랫폼별 의존성을 참조하세요.
충돌하는 드라이버 설치
증상:
같은 환경에서 pyodbc와 함께 mssql-python를 설치한 후 발생하는 가져오기 오류 또는 예기치 않은 동작
해결 방법:
mssql-python 그리고 pyodbc 공존할 수 있습니다. 충돌이 보이면 깔끔한 가상 환경을 만드세요:
python -m venv .venv --clear
.venv\Scripts\activate
pip install mssql-python
연결 문제
서버에 연결할 수 없습니다
증상:
OperationalError: [08001] (0) Client unable to establish connection
가능한 원인 및 해결 방법:
서버에 접속할 수 없습니다
- 서버 이름과 포트가 맞는지 확인하세요.
- 네트워크 연결 확인:
ping servername또는telnet servername 1433. - 방화벽이 포트 1433에서 아웃바운드 연결을 허용하는지 확인하세요.
SQL Server가 실행되지 않음
- SQL Server 서비스가 시작되었는지 확인하세요.
- 명명된 인스턴스의 경우, SQL Server 브라우저 서비스가 실행 중인지 확인하세요.
Azure SQL firewall rules
- Azure 포털에서 Azure SQL 방화벽 규칙에 클라이언트 IP를 추가하세요.
- Azure SQL Managed Instance의 경우, 허용된 네트워크에서 연결하는지 확인하세요.
# Test basic connectivity
import socket
try:
sock = socket.create_connection(("<server>.database.windows.net", 1433), timeout=5)
print("TCP connection successful")
sock.close()
except Exception as e:
print(f"Cannot reach server: {e}")
로그인 실패
증상:
OperationalError: [28000] (18456) Login failed for user 'username'.
가능한 원인 및 해결 방법:
인증 모드 불일치
- Azure SQL Database, Azure SQL Managed Instance, Fabric 내 SQL 데이터베이스의 경우, Microsoft Entra 모드(예:
Authentication=ActiveDirectoryDefault.)를 선호합니다. - SQL 인증을 의도적으로 사용한다면, 서버가 이를 허용하고 해당 엔드포인트에 맞는 올바른 로그인 형식을 사용하고 있는지 확인하세요.
- Azure SQL Database, Azure SQL Managed Instance, Fabric 내 SQL 데이터베이스의 경우, Microsoft Entra 모드(예:
잘못된 SQL 인증 자격 증명
- 사용자 이름과 비밀번호를 확인하세요.
- Azure SQL의 경우 전체 사용자 이름을 포함하세요:
username@servername.
데이터베이스에는 사용자가 존재하지 않습니다
- 사용자가 지정된 데이터베이스에 접근 권한이 있는지 확인하세요.
- 로그인 정보가 데이터베이스 사용자에게 매핑되어 있는지 확인하세요.
인증 설정되지 않음
- Microsoft Entra 인증 사용(권장):
Authentication=ActiveDirectoryDefault. - 로컬 SQL Server가 SQL 인증을 받아야 할 경우 SQL Server가 혼합 모드 인증을 사용하는지 확인하세요.
- Microsoft Entra 인증 사용(권장):
연결 시간 초과
증상:
OperationalError: [HYT00] (0) Timeout expired
OperationalError: [HYT01] (0) Connection timeout expired
가능한 원인 및 해결 방법:
서버가 응답이 느려요
- 연결 타임아웃을 늘리세요:
conn = mssql_python.connect(connection_string, timeout=60)네트워크 대기 시간
- 서버로 가는 네트워크 경로를 확인하세요.
- 더 짧은 네트워크 경로나 VPN을 사용하는 것을 고려해 보세요.
서버 부하가 큰 상황
- 비혼잡 시간대에 연결해 보세요.
- 데이터베이스 관리자에게 연락하세요.
SSL 인증서 오류
증상:
OperationalError: [08001] SSL Provider: The certificate chain was issued by an authority that is not trusted
솔루션:
첫째, 신뢰할 수 있는 인증서나 컨테이너 및 로컬 개발의 로컬 개발 패턴을 선호하세요. 직접 제어하는 서버를 대상으로 하는 로컬 개발에만 TrustServerCertificate=yes를 사용하세요.
자체 서명 인증서를 사용한 개발 및 테스트:
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
"TrustServerCertificate=yes;" # Don't use in production
)
주의
TrustServerCertificate=yes 지역 전용 대체 수단입니다. 공유 개발 컨테이너, CI 파이프라인, 운영 환경에서는 사용하지 마세요. 더 넓은 지침은 암호화 및 인증서를 참조하세요.
생산 시 적절한 인증서가 설치되어 있는지 확인하고 다음을 사용하세요:
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
"HostnameInCertificate=<server>.domain.com;"
)
쿼리 실행 문제
테이블 또는 객체를 찾지 못함
증상:
ProgrammingError: [42S02] (208) Invalid object name 'TableName'.
가능한 원인 및 해결 방법:
잘못된 데이터베이스 컨텍스트
# Ensure you're connected to the correct database cursor.execute("SELECT DB_NAME()") print(cursor.fetchone()[0])스키마는 명시되지 않음
# Use fully qualified name cursor.execute("SELECT * FROM dbo.TableName")테이블은 존재하지 않습니다
# Check if table exists cursor.execute(""" SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'TableName' """)
구문 오류
증상:
ProgrammingError: [42000] (102) Incorrect syntax near '...'.
솔루션:
먼저 SSMS에서 SQL을 테스트 해 문법을 확인하세요
문자열 이스케이핑 확인 - 매개변수화된 쿼리 사용:
# Wrong - vulnerable to syntax issues and SQL injection cursor.execute(f"SELECT * FROM Production.Product WHERE Name = '{name}'") # Correct - use parameters cursor.execute("SELECT * FROM Production.Product WHERE Name = %(name)s", {"name": name})
매개변수 오류
증상:
ProgrammingError: [07001] Wrong number of parameters
솔루션:
자리 표시자와 파라미터를 세세요 - 반드시 일치해야 합니다
적절한 매개변수 스타일 선택:
# Qmark style - positional cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Name LIKE ?", (1, "Adjustable%")) print(cursor.fetchone()) # Pyformat style - named cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(id)s AND Name LIKE %(name)s", {"id": 1, "name": "Adjustable%"}) print(cursor.fetchone())
데이터 타입 문제
날짜 변환 오류
증상:
DataError: [22007] Invalid datetime format
솔루션:
문자열 대신 Python datetime 객체를 사용하세요:
from datetime import datetime
cursor.execute("CREATE TABLE #Events (EventDate DATETIME)")
# Wrong - this raises an error for invalid dates
try:
cursor.execute("INSERT INTO #Events (EventDate) VALUES (%(event_date)s)", {"event_date": "2024-13-45"})
except Exception as e:
print(f"Expected error: {e}")
# Correct - use Python datetime objects
cursor.execute("INSERT INTO #Events (EventDate) VALUES (%(event_date)s)", {"event_date": datetime(2024, 3, 15)})
cursor.execute("SELECT EventDate FROM #Events")
print(cursor.fetchone())
소수점 정밀도 문제
증상:
숫자는 잘렸거나 잘못 반올림된 것처럼 보입니다.
솔루션:
정확한 숫자 값 사용 decimal.Decimal :
from decimal import Decimal
cursor.execute("CREATE TABLE #PriceDemo (ListPrice DECIMAL(10,2))")
# Preserve full precision
cursor.execute(
"INSERT INTO #PriceDemo (ListPrice) VALUES (%(list_price)s)",
{"list_price": Decimal("19.99")}
)
유니코드 인코딩 문제
증상:
특수 문자가 잡음되거나 오류를 일으킵니다.
솔루션:
데이터베이스 내 유니코드 데이터에는 NVARCHAR 열을 사용하세요
문자열을 직접 전달하세요 - 드라이버가 인코딩을 처리합니다:
cursor.execute("CREATE TABLE #UnicodeDemo (Name NVARCHAR(50))") cursor.execute("INSERT INTO #UnicodeDemo (Name) VALUES (%(name)s)", {"name": "日本語"}) cursor.execute("SELECT Name FROM #UnicodeDemo") print(cursor.fetchone())
성능 문제
느린 쿼리 실행
가능한 원인 및 해결 방법:
누락된 인덱스: SSMS에서 쿼리 실행 계획을 확인하세요.
대량 결과 집합:
fetchall()대신fetchmany()을 사용합니다:cursor.arraysize = 1000 while True: rows = cursor.fetchmany() if not rows: break process_rows(rows)연결 풀링 비활성화: 풀링 활성화:
import mssql_python mssql_python.pooling(max_size=20, idle_timeout=300)
큰 결과에서의 메모리 문제
증상:
Python 프로세스는 메모리가 부족합니다.
솔루션:
모든 것을 메모리에 로드하는 대신 스트림 결과를 선택하세요:
cursor.execute("SELECT * FROM LargeTable") for row in cursor: # Iterates one row at a time process_row(row)서버 측 페이지네이션 사용:
page_size = 1000 offset = 0 while True: cursor.execute( "SELECT * FROM LargeTable ORDER BY ID " "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY", (offset, page_size) ) rows = cursor.fetchall() if not rows: break process_rows(rows) offset += page_size
거래 문제
자동 커밋을 이용한 임시 테이블 스코핑
트랜잭션 내에서 생성된 임시 테이블(#tablename)은 트랜잭션이 롤백될 때 사라집니다. 자동 커밋이 꺼져 있을 때(기본값) 이 점이 흔히 혼란스럽습니다:
conn = mssql_python.connect(connection_string) # autocommit=False by default
cursor = conn.cursor()
cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")
# If the connection rolls back (explicit or on error), #TempData disappears
conn.rollback()
# This fails: Invalid object name '#TempData'
cursor.execute("SELECT * FROM #TempData")
수정: 임시 테이블을 생성한 직후 바로 커밋하거나 자동 커밋 모드를 사용하세요:
cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
conn.commit() # Lock in the table definition
cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")
conn.commit()
자동 커밋 모드가 필요한 DDL 문장들, 예: CREATE DATABASE, 는 열린 트랜잭션 내에서 실패합니다. 실행 전에 자동 커밋을 설정하세요:
conn.autocommit = True
cursor.execute("CREATE DATABASE TestDB")
conn.autocommit = False
트랜잭션 미완료
증상:
연결을 닫은 후에는 데이터 변경이 지속되지 않습니다.
Solution:
autocommit=False(기본값)의 경우 commit()를 호출해야 합니다:
cursor.execute("CREATE TABLE #Products (Name NVARCHAR(100))")
cursor.execute("INSERT INTO #Products (Name) VALUES (%(name)s)", {"name": "Widget"})
conn.commit() # Don't forget this!
또는 자동 커밋 모드를 사용하세요:
conn = mssql_python.connect(connection_string, autocommit=True)
교착 상태 오류
증상:
OperationalError: [40001] (1205) Transaction ... was deadlocked on lock resources with another process
Solution:
재시도 논리(재 시도 논리 참조)는 즉각적인 실패를 처리하지만, 반복되는 교착 상태는 설계 문제를 나타냅니다. 근본 원인을 해결하기 위해 교착 상태 그래프를 캡처하고 어떤 문장과 잠금 유형이 관련되어 있는지 분석하세요. 일반적인 해결책으로는 경쟁 트랜잭션이 동일한 순서로 락을 획득하도록 연산 순서를 재조정하고, 트랜잭션 범위를 줄이며, 락 시간을 단축하기 위한 적절한 인덱스 추가가 있습니다.
교착 상태 분석의 전체 공략은 Deadlocks 가이드를 참조하세요. Azure SQL Database를 사용 중이라면, '분석 및 교착 방지'를 참고하세요.
벌크 로드 문제
대량 복사 중 제약 조건 위반
증상:
RuntimeError: CHECK constraint ... Conflict occurred in database ...
RuntimeError: Cannot insert duplicate key ... violation of PRIMARY KEY constraint
원인:
배치 내 데이터는 테이블 제약 조건(기본 키, 고유 키, CHECK, 외래 키)을 위반합니다.
해결 방법:
로드 전에 데이터를 검증하세요. 대규모 데이터셋의 경우, 먼저 스테이징 테이블로 로드한 후 대상 테이블에 병합하세요:
# Load into staging, then validate
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(100))")
cursor.bulkcopy("##Staging", rows)
# Check for duplicates before merging
cursor.execute("""
SELECT s.ID FROM ##Staging s
INNER JOIN dbo.Target t ON s.ID = t.ID
""")
dupes = cursor.fetchall()
if dupes:
print(f"Skipping {len(dupes)} duplicate rows")
# Insert only non-duplicate rows
cursor.execute("""
INSERT INTO dbo.Target (ID, Name)
SELECT s.ID, s.Name FROM ##Staging s
WHERE NOT EXISTS (SELECT 1 FROM dbo.Target t WHERE t.ID = s.ID)
""")
conn.commit()
스테이징 테이블이 포함된 업서트 패턴에 대해서는 데이터 로딩 및 이동 패턴을 참조하세요.
열 매핑 오류
증상:
RuntimeError: Bulk copy failure - column count mismatch
원인:
데이터 내 열 수가 목표 테이블의 열 수와 일치하지 않거나, 열 순서가 잘못되어 있습니다.
해결 방법:
데이터가 테이블 스키마와 순서와 횟수에서 정확히 일치하는지 확인하세요:
# Check the target table schema
cursor.execute("""
SELECT COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'MyTable'
ORDER BY ORDINAL_POSITION
""")
for col in cursor.fetchall():
print(col)
# Match your data to the column order
rows = [
(1, "Widget", Decimal("19.99")), # Must match table column order
(2, "Gadget", Decimal("29.99")),
]
cursor.bulkcopy("dbo.MyTable", rows)
대량 복사 중 형식 불일치
증상:
데이터가 로드되지만 값은 잘려 나오거나 반올림되거나 잘못되었습니다.
원인:
Python 값이 대상 컬럼 타입에 깔끔하게 매핑되지 않습니다. 일반적인 사례: decimal 열에 로드된 float 값(정밀도 손실) 또는 고정 길이 열에 로드된 길이가 초과된 문자열.
해결 방법:
스키마에 맞는 올바른 Python 타입을 사용하세요:
from decimal import Decimal
# Use Decimal for decimal/numeric columns, not float
rows = [
(1, "Widget", Decimal("19.99")), # Correct
# (1, "Widget", 19.99), # Avoid: float loses precision
]
cursor.bulkcopy("dbo.Products", rows)
넘피 타입 결합 실패
증상:
numpy 정수 또는 float 타입을 사용할 때 매개변수는 조용히 실패하거나 데이터 타입 오류를 발생시킵니다.
원인:
numpy.int64 및 numpy.int32와 같은 NumPy 타입은 NumPy 2.x에서 isinstance(x, int)를 통과하지 않습니다. 드라이버의 타입 추론이 이를 인식하지 못해 예상치 못한 행동을 유발합니다.
해결 방법:
numpy 값을 Python 네이티브 타입으로 변환하기 전에 묶으세요:
import numpy as np
# Convert individual values
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(product_id)s", {"product_id": int(np.int64(42))})
# Convert DataFrame values
for _, row in df.iterrows():
cursor.execute(
"INSERT INTO #Orders (ProductID, Qty) VALUES (%(product_id)s, %(qty)s)",
{"product_id": int(row["ProductID"]), "qty": int(row["Qty"])}
)
더 큰 데이터셋의 경우, 내부적으로 타입 변환을 처리하는 Arrow 나 pandas 통합 경로를 사용하세요.
임시 테이블을 이용한 벌크카피
증상:
cursor.bulkcopy("#TempTable", data)에서 RuntimeError: Invalid object name '#TempTable'이 발생합니다.
원인:
bulkcopy() 메타데이터 조회 제한 때문에 세션 임시 테이블(#tablename)을 해결할 수 없습니다. 글로벌 온도 테이블(##tablename)과 영구 테이블이 작동합니다.
해결 방법:
글로벌 온도 테이블이나 일반 스테이징 테이블을 사용하세요:
# Global temp table (visible to all sessions, dropped when last session disconnects)
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("##Staging", rows)
# Or use a permanent staging table
cursor.execute("CREATE TABLE dbo.Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("dbo.Staging", rows)
세션 임시 테이블이 선호되는 소규모 데이터셋의 경우, 다음을 사용 executemany() 하세요:
cursor.execute("CREATE TABLE #Staging (ID INT, Name NVARCHAR(50))")
cursor.executemany("INSERT INTO #Staging (ID, Name) VALUES (?, ?)", rows)
컨테이너 및 CI 문제
리눅스에서 누락된 시스템 라이브러리
증상:
ImportError: libltdl.so.7: cannot open shared object file: No such file or directory
ImportError: libkrb5.so.3: cannot open shared object file
해결 방법:
필요한 시스템 패키지를 설치하세요. 패키지는 배포판에 따라 다릅니다:
| Distribution | 설치 명령 |
|---|---|
| Ubuntu/Debian | sudo apt-get install libltdl7 libkrb5-3 libgssapi-krb5-2 |
| 레드햇 / 페도라 | sudo dnf install libtool-ltdl krb5-libs |
| 알파인 | apk add libltdl krb5-libs |
Dockerfile 예시는 컨테이너 및 로컬 개발을 참조하세요.
macOS 설치 후 SSL 오류
증상:
macOS에서 연결할 때, 특히 Apple Silicon에서 SSL 관련 오류가 발생합니다.
해결 방법:
Homebrew를 통해 OpenSSL을 설치하고 링커 플래그를 설정하세요:
brew install openssl
export LDFLAGS="-L/opt/homebrew/opt/openssl/lib"
export CPPFLAGS="-I/opt/homebrew/opt/openssl/include"
진단 도구
드라이버 로깅 활성화
문제 해결을 위한 포괄적인 디버그 로깅을 활성화하는 데 사용 mssql_python.setup_logging() 하세요. SQL 문, 매개변수, 내부 ODBC 작업, 연결 상태 변경 등 모든 드라이버 작업이 기록됩니다.
import mssql_python
# Enable logging to file (default)
mssql_python.setup_logging()
# Output to stdout (useful for CI/CD and containers)
mssql_python.setup_logging(output='stdout')
# Output to both file and stdout
mssql_python.setup_logging(output='both')
# Custom log file path (must use .txt, .log, or .csv extension)
mssql_python.setup_logging(log_file_path="/var/log/myapp/mssql.log")
로그 파일은 CSV 형식으로 작성되며, 512MB 크기로 자동으로 회전하며 다섯 개의 백업을 제공합니다. 비밀번호나 접근 토큰과 같은 민감한 데이터는 로그 출력에서 자동으로 정제됩니다.
운전자 로그와 함께 자신의 로그 항목을 추가하려면 다음을 사용하세요 driver_logger:
from mssql_python.logging import driver_logger
mssql_python.setup_logging()
driver_logger.debug("[App] Starting data processing")
driver_logger.error("[App] Failed to process record")
# Your entries appear in the same file with the same format
주의
로깅에는 성능 오버헤드가 있습니다. 기본적으로 운영 환경에서는 활성화하지 말고, 문제 해결 시에만 활성화하세요.
운전자 정보 받기
활성 연결에서 드라이버 버전과 서버 정보를 조회하세요:
import mssql_python
conn = mssql_python.connect(connection_string)
# Driver version
print(f"Version: {mssql_python.__version__}")
# Server information
print(f"Server name: {conn.getinfo(mssql_python.SQL_SERVER_NAME)}")
print(f"Database name: {conn.getinfo(mssql_python.SQL_DATABASE_NAME)}")
연결 상태 확인
연산 시도 전에 연결이 아직 열려 있는지 확인하세요:
try:
cursor = conn.cursor()
cursor.execute("SELECT 1")
print("Connection is open")
except mssql_python.Error:
print("Connection is closed or broken")
빠른 참고: 흔한 오류
| 오류 | SQLSTATE | 일반적 원인 | 빠른 수정 |
|---|---|---|---|
| 클라이언트가 연결을 설정할 수 없음 | 08001 | 서버에 연결할 수 없음 | 서버 이름/포트 확인 |
| 로그인 실패 | 28000 | 잘못된 자격증 | 사용자 이름/비밀번호 확인해 |
| 시간 제한 만료됨 | HYT00/HYT01 | 느린 네트워크 | 시간 제한 늘리기 |
| 잘못된 개체 이름 | 42S02 | 잘못된 테이블/스키마 | 완전히 정식 명칭을 사용하세요 |
| 구문 오류 | 42000 | SQL 오류 | 매개 변수가 있는 쿼리 사용 |
| 제약 조건 위반 | 23000 | FK/PK 위반 | 데이터 무결성 점검 |
| 교착 상태 | 40001 | 잠금 경합 | 다시 시도한 후 교착 상태 그래프를 분석합니다 |