문자열 및 유니코드 처리

Microsoft SQL은 mssql-python 드라이버가 Python str 객체에 매핑하는 여러 문자열 유형을 제공합니다. 핵심 결정은 (비유니코드) 또는 varchar (유니코드) 중 어느 것을 사용 nvarchar 할지입니다:

  • nvarchar 이름, 주소, 사용자 생성 콘텐츠 등 ASCII 외의 문자가 포함된 데이터에는 사용하세요.
  • 데이터가 엄격히 ASCII(코드, 식별자, 이메일 주소)일 때 저장 공간을 절약하고 싶을 때 사용 varchar 하세요. varchar 문자당 1바이트를 사용하며; nvarchar 문자당 2바이트를 사용합니다.
SQL 형식 Unicode 최대 길이 Python 형식
char(n) 아니오 8,000 str
varchar(n) 아니오 8,000 str
varchar(max) 아니오 2GB str
nchar(n) Yes 4,000 str
nvarchar(n) Yes 4,000 str
nvarchar(max) Yes 2GB str
text 아니오 2GB (폐기됨) str
ntext Yes 2GB (폐기됨) str

기본적인 문자열 작업

이 드라이버는 모든 Microsoft SQL 문자열 타입을 Python str 객체에 매핑합니다.

문자열 삽입 및 가져오기

매개변수화된 쿼리를 사용하여 데이터베이스에서 문자열 데이터를 안전하게 삽입하고 가져오세요.

import mssql_python

conn = mssql_python.connect(connection_string)
cursor = conn.cursor()

# Create temp table for demo
cursor.execute("""
    CREATE TABLE #StringDemo (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        Name NVARCHAR(100),
        Email NVARCHAR(200)
    )
""")

# Insert string data
cursor.execute(
    "INSERT INTO #StringDemo (Name, Email) VALUES (%(name)s, %(email)s)",
    {"name": "Alice Smith", "email": "alice@example.com"}
)
conn.commit()

# Retrieve string data
cursor.execute("SELECT Name, Email FROM #StringDemo WHERE ID = 1")
row = cursor.fetchone()
print(row.Name)   # 'Alice Smith'
print(row.Email)  # 'alice@example.com'

특수 문자를 가진 문자열

매개변수화된 쿼리를 사용하여 따옴표, 괄호 및 기타 특수 문자를 처리합니다.

# Quotes and special characters handled automatically
cursor.execute("""
    CREATE TABLE #Notes (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        Title NVARCHAR(200),
        Content NVARCHAR(MAX)
    )
""")
cursor.execute(
    "INSERT INTO #Notes (Title, Content) VALUES (%(title)s, %(content)s)",
    {
        "title": "O'Brien's Report",
        "content": 'Contains "quotes" and special chars: <>&'
    }
)
conn.commit()

유니코드 지원

nvarchar 컬럼과 Python을 str 사용해 어떤 언어든 텍스트를 저장하고 검색하세요.

유니코드 텍스트를 저장하세요

유니코드 콘텐츠를 매개변수화된 쿼리에 Python 문자열을 전달하여 삽입합니다; 드라이버는 nvarchar 열에 대해 UTF-16LE로 인코딩합니다.

# International characters - use nvarchar columns
cursor.execute("""
    CREATE TABLE #Messages (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        Content NVARCHAR(MAX)
    )
""")
cursor.execute("""
    INSERT INTO #Messages (Content) VALUES (%(msg)s)
""", {"msg": "Hello 你好 مرحبا שלום 🎉"})

cursor.execute("SELECT Content FROM #Messages WHERE ID = 1")
row = cursor.fetchone()
print(row.Content)  # 'Hello 你好 مرحبا שלום 🎉'

다양한 문자에서의 유니코드

nvarchar 컬럼과 벌크 인서트를 사용하여 단일 테이블에서 여러 언어와 스크립트를 지원합니다.

messages = [
    {"lang": "English", "text": "Hello, World!"},
    {"lang": "Chinese", "text": "你好,世界!"},
    {"lang": "Japanese", "text": "こんにちは世界!"},
    {"lang": "Korean", "text": "안녕하세요, 세상!"},
    {"lang": "Arabic", "text": "مرحبا بالعالم!"},
    {"lang": "Hebrew", "text": "שלום עולם!"},
    {"lang": "Russian", "text": "Привет мир!"},
    {"lang": "Greek", "text": "Γειά σου Κόσμε!"},
    {"lang": "Emoji", "text": "👋🌍✨🎉"},
]

cursor.execute("""
    CREATE TABLE #Greetings (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        Language NVARCHAR(50),
        Message NVARCHAR(200)
    )
""")
cursor.executemany("""
    INSERT INTO #Greetings (Language, Message) VALUES (%(lang)s, %(text)s)
""", messages)
conn.commit()

Unicode용 nvarchar 열을 반드시 확인하세요

데이터에 ASCII가 아닌 문자가 포함될 수 있을 때는 항상 varchar 대신 nvarchar로 열을 정의하세요.

-- For Unicode data, always use nvarchar, not varchar
CREATE TABLE #UnicodeDemo (
    ID INT IDENTITY PRIMARY KEY,
    Name NVARCHAR(100),        -- Supports Unicode
    Description NVARCHAR(MAX)  -- Supports large Unicode text
);

문자열 길이 고려 사항

데이터 길이의 일관성에 따라 고정 길이와 가변 길이 유형 중 선택하세요.

고정 길이와 가변 길이

Microsoft SQL은 char(n) 선언된 길이에 맞춰 뒤에 공백을 넣어 값을 추가합니다. 이 패딩은 가변 길이 데이터의 저장 공간을 낭비하지만, 국가 코드와 같은 고정 폭 열의 성능은 향상시킬 수 있습니다. 대부분의 문자열 열에는 varchar(n)를 사용하세요.

다음 예시는 패딩 열과 비패딩 열이 데이터 검색을 처리하는 방식을 보여줍니다:

# char(6) pads to fixed length
cursor.execute(
    "SELECT StateProvinceCode FROM Person.StateProvince WHERE StateProvinceID = 1"
)  # nchar(6) column
row = cursor.fetchone()
print(repr(row.StateProvinceCode))  # 'AB    ' - right-padded with spaces

# nvarchar stores actual length
cursor.execute(
    "SELECT Name FROM Person.StateProvince WHERE StateProvinceID = 1"
)  # nvarchar column
row = cursor.fetchone()
print(repr(row.Name))  # 'Alberta' - no padding

트레일링 스페이스 처리

고정 길이 문자 열에서 데이터를 가져올 때, Microsoft SQL Server가 추가한 패딩 공간을 제거하기 위해 사용 rstrip() 하세요.

# Strip trailing spaces from char columns
cursor.execute("SELECT ProductNumber FROM Production.Product")
for row in cursor:
    code = row.ProductNumber.rstrip()  # Remove trailing spaces
    print(f"Code: '{code}'")

긴 문자열(MAX 형식)

and nvarchar(max) 타입은 varchar(max) 최대 2GB의 문자열을 지원하여 대용량 문서, JSON 또는 XML 콘텐츠 저장에 이상적입니다.

# Large text content
large_content = "x" * 100000  # 100K characters

cursor.execute("""
    CREATE TABLE #Documents (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        Content NVARCHAR(MAX)
    )
""")
cursor.execute("""
    INSERT INTO #Documents (Content) VALUES (%(content)s)
""", {"content": large_content})

cursor.execute("SELECT Content FROM #Documents WHERE ID = 1")
row = cursor.fetchone()
print(len(row.Content))  # 100000

문자열 비교 및 정렬

Microsoft SQL 문자열 비교 동작은 데이터베이스 또는 컬럼의 콜레이션 집합에 따라 달라집니다.

대/소문자 구분

Microsoft SQL 문자열 비교는 콜레이션에 의존합니다. 기본적으로 대부분의 데이터베이스는 대소문자를 구분하지 않는 콜레이션을 사용하지만, COLLATE 절을 사용하여 이를 재정의할 수 있습니다.

# Case-insensitive collation (default for many databases)
cursor.execute("SELECT * FROM Person.Person WHERE LastName = %(name)s", {"name": "smith"})
# Might match 'Smith', 'SMITH', 'smith' depending on collation

# For case-sensitive comparison
cursor.execute("""
    SELECT * FROM Person.Person 
    WHERE LastName COLLATE Latin1_General_CS_AS = %(name)s
""", {"name": "Smith"})

LIKE 패턴 일치

와일드카드 문자와 함께 LIKE 연산자를 사용하여 문자열 패턴을 검색하고, 특수 문자는 대괄호 표기법으로 이스케이프하여 리터럴 문자와 일치시키십시오.

# Wildcard searches
search_term = "Road"
cursor.execute("""
    SELECT Name FROM Production.Product WHERE Name LIKE %(pattern)s
""", {"pattern": f"%{search_term}%"})

# Escape special characters in search
def escape_like(value: str) -> str:
    """Escape LIKE wildcards in search value."""
    return value.replace("[", "[[]").replace("%", "[%]").replace("_", "[_]")

search = "100%"
cursor.execute("""
    SELECT Name FROM Production.Product WHERE Name LIKE %(pattern)s
""", {"pattern": f"%{escape_like(search)}%"})

인코딩 고려사항

인코딩 동작은 Microsoft SQL 열 유형과 소스 콜레이션에 따라 달라집니다.

인코딩 가정과 유니코드 기본값

드라이버는 mssql-python Microsoft SQL 열 타입을 기반으로 자동으로 인코딩을 처리합니다. 기본적으로 nvarchar 열에 대해 문자열 매개변수는 UTF-16LE로 전송되며, varchar 열에 대한 데이터베이스 정렬에 따라 다음과 같습니다:

열 형식 와이어 인코딩 Python 결과
nvarchar, nchar, ntext UTF-16LE str (드라이버에 의해 해독됨)
varchar, char, text 데이터베이스 또는 열 정렬 인코딩 str (드라이버가 소스 인코딩을 사용해 디코딩)

Python 문자열은 내부적으로 항상 유니코드입니다. 매개변수를 str 전달하면 드라이버가 대상 컬럼 유형에 맞게 인코딩합니다. 기본적으로 드라이버는 문자열 매개변수를 (유니코드)로 nvarchar 전송하므로, 데이터베이스 콜레이션과 상관없이 문자가 보존됩니다. 열의 경우 varchar , UTF-8은 데이터베이스나 열이 UTF-8 지원 콜레이션을 사용할 때만 적용됩니다.

열이 varchar 이고 열 형식과 정확히 일치하도록 비유니코드 데이터를 보내야 하는 경우(예: 암시적 변환 경고를 피하기 위해), setinputsizes()를 사용하여 기본값을 재정의하세요:

import mssql_python

conn = mssql_python.connect(connection_string)
cursor = conn.cursor()

# Create temp table for demo
cursor.execute("CREATE TABLE #AsciiTable (Code VARCHAR(100))")

cursor.setinputsizes([(mssql_python.SQL_VARCHAR, 100, 0)])
cursor.execute(
    "INSERT INTO #AsciiTable (Code) VALUES (?)",
    ("ABC123",)
)
conn.commit()

대부분의 애플리케이션에서는 기본 동작이 올바릅니다. 쿼리 계획에서 암묵적인 전환 경고가 보이거나 특정 varchar 콜레이션과 일치해야 할 때만 오버라이드하세요.

연결 인코딩

mssql-python 드라이버는 Microsoft SQL Server 버전과 구성을 기반으로 연결에 대한 인코딩을 자동으로 처리합니다. Python 문자열은 유니코드이기 때문에 드라이버가 대상 데이터 타입에 맞게 적절히 인코딩합니다(UTF-8 또는 UTF-16). 연결 인코딩을 수동으로 설정할 필요는 없습니다.

레거시 콜레이션이 포함된 VARCHAR 열들

Latin1_General_CI_AS와 같은 Windows-1252(CP1252) 콜레이션을 사용하는 데이터베이스는 확장 라틴 문자(예: , 및 악센트가 있는 문자)를 CP1252 인코딩을 사용하는 varchar 열에 저장합니다. 드라이버는 모든 플랫폼에서 이 문자들을 올바르게 해독합니다.

이 차이는 크로스 플랫폼 배포에 중요합니다: Windows에서 올바르게 읽히는 동일한 varchar 데이터가 Linux에서도 올바르게 읽히며, 특별한 설정이 필요하지 않습니다.

# Create a temp table with a varchar column and insert extended Latin characters
cursor.execute("CREATE TABLE #Products (Name VARCHAR(100))")
cursor.execute("INSERT INTO #Products (Name) VALUES (%(name)s)", {"name": "Café €100 ™"})
conn.commit()

# CP1252 characters in varchar columns are decoded correctly on all platforms
cursor.execute("SELECT Name FROM #Products WHERE Name LIKE '%€%'")
for row in cursor:
    print(row.Name)  # Correct on both Windows and Linux

스키마에서 허용된다면 varchar 열을 nvarchar(으)로 마이그레이션하면 인코딩 모호성을 완전히 없애고 모든 유니코드 문자를 지원할 수 있습니다.

파일 인코딩

데이터베이스에 삽입할 파일을 읽을 때는 유니코드 내용을 보존할 적절한 인코딩을 지정하세요.

# Reading files with explicit encoding
def insert_file_content(cursor, conn, file_path: str, encoding: str = "utf-8"):
    with open(file_path, "r", encoding=encoding) as f:
        content = f.read()
    
    cursor.execute(
        "INSERT INTO #FileContent (Content) VALUES (%(content)s)",
        {"content": content}
    )
    conn.commit()

일반적인 문자열 작업

이 예시들은 Python과 SQL 모두에서 흔히 사용되는 문자열 조작 패턴을 다룹니다.

연결하기

문자열은 삽입 전에 Python에서 연결하거나, 서버에서 SQL의 문자열 연산자를 사용하여 연결할 수 있습니다.

# Concatenate in Python before insert
first_name = "Alice"
last_name = "Smith"
full_name = f"{first_name} {last_name}"

cursor.execute("""
    CREATE TABLE #ConcatDemo (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        FullName NVARCHAR(200)
    )
""")
cursor.execute(
    "INSERT INTO #ConcatDemo (FullName) VALUES (%(name)s)",
    {"name": full_name}
)

# Or concatenate in SQL
cursor.execute("""
    SELECT FirstName + ' ' + LastName AS FullName FROM Person.Person
""")

문자열 서식 지정

사용자에게 표시하기 전에 Python에서 문자열에 통화, 패딩 또는 정렬 서식을 적용하세요.

from decimal import Decimal

# Format for display
cursor.execute("SELECT Name, ListPrice FROM Production.Product WHERE ListPrice > 0")
for row in cursor.fetchall()[:5]:
    print(f"{row.Name}: ${row.ListPrice:.2f}")

# Pad strings
cursor.execute("SELECT ProductNumber FROM Production.Product")
for row in cursor.fetchall()[:5]:
    padded = row.ProductNumber.ljust(15)  # Left-justify, pad to 15 chars
    print(f"[{padded}]")

NULL 대 빈 문자열

Microsoft SQL은 NULL과 빈 문자열('')을 서로 다른 값으로 취급합니다. NULL은 "알 수 없음"을 의미하고, empty 문자열은 "빈 것으로 알려진 것"을 의미합니다. 지원서에 맞는 하나의 관습을 선택하고 일관성을 유지하세요. 대부분의 애플리케이션은 누락된 선택적 필드에 대해 NULL을 사용합니다.

다음 예시는 NULL과 빈 문자열을 구분하는 방법을 보여줍니다:

# NULL is different from empty string
cursor.execute("""
    CREATE TABLE #NullDemo (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        Name NVARCHAR(100),
        MiddleName NVARCHAR(100)
    )
""")
cursor.execute("""
    INSERT INTO #NullDemo (Name, MiddleName) 
    VALUES (%(name)s, %(middle)s)
""", {"name": "Alice", "middle": None})  # NULL

cursor.execute("""
    INSERT INTO #NullDemo (Name, MiddleName) 
    VALUES (%(name)s, %(middle)s)
""", {"name": "Bob", "middle": ""})  # Empty string

# Query differences
cursor.execute("SELECT * FROM #NullDemo WHERE MiddleName IS NULL")
cursor.execute("SELECT * FROM #NullDemo WHERE MiddleName = ''")

다듬기 작업

Python의 문자열 메서드를 사용해 데이터베이스에서 가져온 값에서 앞 공백, 후방 또는 둘 다 제거하세요.

cursor.execute("SELECT Name FROM Production.Product")
for row in cursor:
    # Remove whitespace
    trimmed = row.Name.strip()  # Both ends
    left_trimmed = row.Name.lstrip()
    right_trimmed = row.Name.rstrip()

JSON 문자열 데이터

JSON 문서를 nvarchar(max) 열에 저장하고 Microsoft SQL의 JSON 함수로 쿼리하세요.

Store JSON as nvarchar

Python 딕셔너리를 JSON 문자열로 직렬화하여 nvarchar 열에 삽입하고, 다시 가져와 Python 객체로 역직렬화합니다.

import json

data = {"name": "Alice", "scores": [95, 87, 91], "active": True}
json_string = json.dumps(data)

cursor.execute("""
    CREATE TABLE #Configs (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        ConfigData NVARCHAR(MAX)
    )
""")
cursor.execute("""
    INSERT INTO #Configs (ConfigData) VALUES (%(data)s)
""", {"data": json_string})

# Retrieve and parse
cursor.execute("SELECT ConfigData FROM #Configs WHERE ID = 1")
row = cursor.fetchone()
config = json.loads(row.ConfigData)
print(config["name"])  # 'Alice'

Microsoft SQL JSON 함수 사용

Microsoft SQL의 JSON 함수를 사용해 클라이언트 코드 대신 쿼리에서 JSON 데이터를 직접 파싱하고 필터링하세요.

import json

data = {"name": "Alice", "scores": [95, 87, 91], "active": True}

cursor.execute("""
    CREATE TABLE #Configs (
        ID INT IDENTITY(1,1) PRIMARY KEY,
        ConfigData NVARCHAR(MAX)
    )
""")
cursor.execute(
    "INSERT INTO #Configs (ConfigData) VALUES (%(data)s)",
    {"data": json.dumps(data)}
)
conn.commit()

cursor.execute("""
    SELECT JSON_VALUE(ConfigData, '$.name') AS Name
    FROM #Configs
    WHERE JSON_VALUE(ConfigData, '$.active') = 'true'
""")
for row in cursor:
    print(row.Name)  # 'Alice'

패턴 매칭에 사용 LIKE 하거나, 더 고급 텍스트 검색을 위해 전체 텍스트 색인을 활성화할 수 있습니다.

전체 텍스트 쿼리

와일드카드 패턴을 사용하는 LIKE 연산자는 전문 인덱스를 사용할 수 없을 때 전문 검색을 위한 간편한 대안이 될 수 있습니다.

# Using CONTAINS (requires full-text index on the table)
cursor.execute("""
    SELECT JobTitle FROM HumanResources.Employee
    WHERE JobTitle LIKE %(search)s
""", {"search": "%Engineer%"})

# Pattern-based search as an alternative to full-text
cursor.execute("""
    SELECT Name FROM Production.Product
    WHERE Name LIKE %(search)s
""", {"search": "%Mountain%"})

모범 사례

이 지침을 적용하여 언어와 인코딩 간에 문자열 데이터를 올바르게 처리하세요.

국제 데이터에 nvarchar를 사용하세요

열에 유니코드가 포함되어 있을 수 있는지 확실하지 않다면 nvarchar를 사용하세요. 저장 공간 비용은 적당하며 문자 변환으로 인한 데이터 손실을 방지합니다.

다음 예시는 유니코드 데이터와 ASCII 전용 데이터의 칼럼 정의 차이를 보여줍니다:

-- Good: supports any language
CREATE TABLE #UserProfile (
    Name NVARCHAR(100),
    Bio NVARCHAR(MAX)
);

-- Limited: ASCII/Latin only
CREATE TABLE #UserProfileAscii (
    Name VARCHAR(100),
    Bio VARCHAR(MAX)
);

문자열 길이 검증

삽입 전에 Python에서 문자열 길이를 확인하여 절단 오류를 방지하고 사용자에게 의미 있는 오류 메시지를 제공하세요.

def safe_insert(cursor, name: str, max_length: int = 100):
    """Insert with length validation."""
    if len(name) > max_length:
        raise ValueError(f"Name exceeds {max_length} characters")
    
    cursor.execute(
        "INSERT INTO #UserProfile (Name) VALUES (%(name)s)",
        {"name": name}
    )

이진 문자열은 별도로 처리하세요

인코딩 문제를 피하기 위해 텍스트 문자열(Pythonstr, SQL nvarchar)과 이진 데이터(Pythonbytes, SQL varbinary등)를 구분하세요.

binary_data = b'\x00\x01\x02'  # bytes - use varbinary
text_data = "Hello"            # str - use nvarchar