Quickstart: Apache Arrow med mssql-python-drivrutinen för Python

I den här snabbstarten använder du mssql-python drivrutinens inbyggda Arrow-metoder för hämtning för att hämta SQL Server-data som kolumnbaserade Apache Arrow-tabeller. Arrows kolumnminnesformat möjliggör högpresterande analys, nollkopieringsinterop med pandas, Polars och DuckDB, samt effektiv Parquet-fil-I/O utan rad-för-rad Python-objektskapande.

Drivrutinen mssql-python kräver inga externa beroenden på Windows-datorer. Drivrutinen installerar allt den behöver med en enda pip installation, så du kan använda den senaste versionen av drivrutinen för nya skript utan att förstöra andra skript som du inte har tid att uppgradera och testa.

mssql-python-dokumentation | mssql-python-källkod | Paket (PyPI) | Uv

Förutsättningar

Installera operativsystemsspecifika engångsförutsättningar. Windows-användare kan hoppa över detta steg. För fullständiga plattformsdetaljer, se Installera mssql-python.

apk add libtool krb5-libs krb5-dev

Skapa en SQL-databas

Skapa eller koppla till en SQL-databas på en av följande plattformar:

Skapa projektet och kör koden

  1. Skapa ett nytt projekt
  2. Lägga till beroenden
  3. Starta Visual Studio Code
  4. Uppdatera pyproject.toml
  5. Uppdatera main.py
  6. Spara anslutningssträngen
  7. Använd uv run för att köra skriptet

Skapa ett nytt projekt

  1. Öppna en kommandotolk i utvecklingskatalogen. Om du inte har någon, skapa en ny katalog, till exempel python eller scripts. Undvik mappar på din OneDrive, eftersom synkronisering kan störa hanteringen av din virtuella miljö.

  2. Skapa ett nytt projekt genom att använda uv.

    uv init arrow-qs
    cd arrow-qs
    

Lägga till beroenden

I samma katalog, installera mssql-python, python-dotenv, pyarrow, och rich paket.

uv add mssql-python python-dotenv pyarrow rich

Öppna Visual Studio Code

Kör följande kommando i samma katalog.

code .

Uppdatera pyproject.toml

  1. Filen pyproject.toml innehåller metadata för ditt projekt. Öppna filen i din favoritredigerare.

  2. Granska innehållet i filen. Det bör likna det här exemplet. Notera Python-versionen och beroendet för mssql-python. Använd >= för att ange en lägsta version. Om du föredrar en exakt version, ändra >= före versionsnumret till ==. De lösta versionerna av varje paket lagras sedan i uv.lock. Låsfilen säkerställer att utvecklare som arbetar med projektet använder konsekventa paketversioner. Checka in både pyproject.toml och uv.lock och kör ett organisationsgodkänt verktyg för beroendeskanning i CI. Redigera inte uv.lock filen direkt.

    [project]
    name = "arrow-qs"
    version = "0.1.0"
    description = "Add your description here"
    readme = "README.md"
    requires-python = ">=3.11"
    dependencies = [
        "mssql-python>=1.5.0",
        "pyarrow>=19.0.0",
        "python-dotenv>=1.1.1",
        "rich>=14.1.0",
    ]
    
  3. Uppdatera beskrivningen så att den blir mer beskrivande.

    description = "Fetch SQL Server data as Apache Arrow tables using mssql-python"
    
  4. Spara och stäng filen.

Uppdatera main.py

  1. Öppna filen med namnet main.py. Det bör likna det här exemplet.

    def main():
        print("Hello from arrow-qs!")
    
    if __name__ == "__main__":
        main()
    
  2. Ersätt hela innehållet i main.py med följande kod.

    """Fetch SQL Server data as Apache Arrow tables using mssql-python."""
    
    from os import getenv
    
    import pyarrow as pa
    import pyarrow.parquet as pq
    from dotenv import load_dotenv
    from mssql_python import connect, Connection
    from rich.console import Console
    from rich.table import Table
    
    console = Console()
    
    
    def get_connection() -> Connection:
        """Create a connection using the connection string from .env."""
        load_dotenv()
        conn_str = getenv("SQL_CONNECTION_STRING")
        if not conn_str:
            raise ValueError("SQL_CONNECTION_STRING not set in .env file")
        return connect(conn_str)
    
    
    def fetch_arrow_table(conn: Connection) -> pa.Table:
        """Run a query and return the full result as an Arrow Table."""
        cursor = conn.cursor()
        cursor.execute("""
            SELECT
                p.ProductID,
                p.Name,
                p.ProductNumber,
                p.Color,
                p.StandardCost,
                p.ListPrice,
                p.Size,
                p.Weight,
                p.SellStartDate,
                pc.Name AS Category
            FROM SalesLT.Product AS p
            INNER JOIN SalesLT.ProductCategory AS pc
                ON p.ProductCategoryID = pc.ProductCategoryID
            ORDER BY p.ListPrice DESC
        """)
        arrow_table = cursor.arrow()
        cursor.close()
        return arrow_table
    
    
    def fetch_arrow_batches(conn: Connection) -> pa.Table:
        """Stream results one batch at a time using arrow_batch()."""
        cursor = conn.cursor()
        cursor.execute("""
            SELECT
                c.CustomerID,
                c.CompanyName,
                c.EmailAddress,
                COUNT(soh.SalesOrderID) AS OrderCount,
                SUM(soh.SubTotal + soh.TaxAmt + soh.Freight) AS TotalSpent
            FROM SalesLT.Customer AS c
            LEFT OUTER JOIN SalesLT.SalesOrderHeader AS soh
                ON c.CustomerID = soh.CustomerID
            GROUP BY
                c.CustomerID,
                c.CompanyName,
                c.EmailAddress
            ORDER BY TotalSpent DESC
        """)
        batches = []
        while True:
            batch = cursor.arrow_batch()
            if batch is None or batch.num_rows == 0:
                break
            batches.append(batch)
        cursor.close()
    
        if not batches:
            return pa.table({})
    
        return pa.Table.from_batches(batches)
    
    
    def fetch_with_reader(conn: Connection) -> pa.Table:
        """Use arrow_reader() to stream results as a RecordBatchReader."""
        cursor = conn.cursor()
        cursor.execute("""
            SELECT
                soh.SalesOrderID,
                soh.OrderDate,
                (soh.SubTotal + soh.TaxAmt + soh.Freight) AS TotalDue,
                c.CompanyName
            FROM SalesLT.SalesOrderHeader AS soh
            INNER JOIN SalesLT.Customer AS c
                ON soh.CustomerID = c.CustomerID
            ORDER BY soh.OrderDate DESC
        """)
        reader = cursor.arrow_reader()
        arrow_table = reader.read_all()
        cursor.close()
        return arrow_table
    
    
    def display_arrow_table(arrow_table: pa.Table, title: str, max_rows: int = 10) -> None:
        """Display an Arrow table using rich formatting."""
        rich_table = Table(title=title)
    
        for name in arrow_table.column_names:
            rich_table.add_column(name, style="bright_white")
    
        for i in range(min(max_rows, arrow_table.num_rows)):
            row = [str(arrow_table.column(col)[i].as_py()) for col in range(arrow_table.num_columns)]
            rich_table.add_row(*row)
    
        if arrow_table.num_rows > max_rows:
            rich_table.add_row(*[f"... ({arrow_table.num_rows - max_rows} more rows)" if col == 0 else "" for col in range(arrow_table.num_columns)])
    
        console.print(rich_table)
        console.print(f"\n[dim]Schema: {arrow_table.num_columns} columns, {arrow_table.num_rows} rows[/dim]\n")
    
    
    def save_to_parquet(arrow_table: pa.Table, file_path: str) -> None:
        """Save an Arrow table to a Parquet file."""
        pq.write_table(arrow_table, file_path)
        console.print(f"[green]Saved {arrow_table.num_rows} rows to {file_path}[/green]\n")
    
    
    def main() -> None:
        conn = get_connection()
    
        # 1. Fetch entire result as an Arrow Table with cursor.arrow()
        console.rule("[bold]cursor.arrow() - Full table fetch[/bold]")
        products = fetch_arrow_table(conn)
        display_arrow_table(products, "Products (Top 10 by List Price)")
    
        # 2. Stream results in batches with cursor.arrow_batch()
        console.rule("[bold]cursor.arrow_batch() - Batch streaming[/bold]")
        customers = fetch_arrow_batches(conn)
        display_arrow_table(customers, "Customers by Total Spent")
    
        # 3. Use RecordBatchReader with cursor.arrow_reader()
        console.rule("[bold]cursor.arrow_reader() - RecordBatchReader[/bold]")
        orders = fetch_with_reader(conn)
        display_arrow_table(orders, "Recent Orders")
    
        # 4. Save to Parquet
        console.rule("[bold]Save to Parquet[/bold]")
        save_to_parquet(products, "products.parquet")
    
        # 5. Read back from Parquet and verify
        loaded = pq.read_table("products.parquet")
        console.print(f"[green]Read back {loaded.num_rows} rows from products.parquet[/green]")
        console.print(f"[dim]Schema: {loaded.schema}[/dim]\n")
    
        conn.close()
    
    
    if __name__ == "__main__":
        main()
    

Spara anslutningssträngen

  1. .gitignore Öppna filen och lägg till ett undantag för .env filer. Filen bör likna det här exemplet. Se till att spara och stänga den när du är klar.

    # Python-generated files
    __pycache__/
    *.py[oc]
    build/
    dist/
    wheels/
    *.egg-info
    
    # Virtual environments
    .venv
    
    # Connection strings and secrets
    .env
    
    # Generated data files
    *.parquet
    
  2. I den aktuella katalogen skapar du en ny fil med namnet .env.

  3. I .env-filen lägger du till en post för din anslutningssträng med namnet SQL_CONNECTION_STRING. Ersätt exemplet här med det faktiska anslutningssträngsvärdet.

    SQL_CONNECTION_STRING="Server=<server_name>;Database=<database_name>;Encrypt=yes;TrustServerCertificate=no;Authentication=ActiveDirectoryInteractive"
    

    Important

    Håll .env lokalt och utanför versionshantering. För CI och distribuerade miljöer, injicera reťazec pripojenia eller dess komponenthemligheter från din plattforms hemliga lagring istället för att kopiera .env mellan maskiner.

    Tip

    Den reťazec pripojenia du använder beror till stor del på vilken typ av SQL-databas du ansluter till. Om du ansluter till en Azure SQL Database eller en SQL-databas i Fabric använder du ODBC-anslutningssträngen från fliken Anslutningssträngar. Du kan behöva justera autentiseringstypen beroende på ditt scenario. Mer information om anslutningssträngar och deras syntax finns i referens för anslutningssträngssyntax.

Använd uv run för att köra skriptet

Tip

På macOS fungerar både ActiveDirectoryInteractive och ActiveDirectoryDefault för Microsoft Entra-autentisering. ActiveDirectoryInteractive uppmanar dig att logga in varje gång du kör skriptet. För att undvika upprepade inloggningspromptar, logga in en gång via Azure CLI genom att köra az login, och använd ActiveDirectoryDefaultsedan , vilket återanvänder den cachade legitimationen.

  • Kör följande kommando i terminalfönstret från tidigare eller ett nytt terminalfönster som är öppet till samma katalog.

    uv run main.py
    

    Skriptet visar tre metoder för att hämta Arrow:

    • cursor.arrow() returnerar en komplett pyarrow.Table med alla rader. Bäst för små till medelstora resultatuppsättningar där du behöver hela datamängden i minnet.

    • cursor.arrow_batch() returnerar en pyarrow.RecordBatch i taget. Bäst för stora resultatuppsättningar där du vill bearbeta data inkrementellt utan att ladda in allt i minnet.

    • cursor.arrow_reader() returnerar en pyarrow.RecordBatchReader för strömning. Bäst för bearbetning i pipeline-stil eller för att skicka vidare direkt till bibliotek som accepterar en Reader.

    Skriptet sparar också produktdata i en Parquet-fil och läser tillbaka den för att verifiera rundturen.

Så här fungerar koden

  1. Connection: Skriptet laddar reťazec pripojenia från en .env fil och skapar en anslutning med .mssql_python.connect()

  2. Fullständig tabellhämtning: cursor.arrow() kör frågan och returnerar hela resultatmängden som en pyarrow.Table. Drivrutinen konverterar data i sitt C++-lager med hjälp av Arrow C Data Interface, vilket kringgår Python-objektskapandet för förbättrad prestanda.

  3. Batch-streaming: cursor.arrow_batch() returnerar en pyarrow.RecordBatch per samtal. Loopen samlar batcher tills inga rader finns kvar, och kombinerar dem sedan till en enda tabell. Använd detta tillvägagångssätt för stora datamängder eller när du vill bearbeta varje batch självständigt.

  4. RecordBatchReader: cursor.arrow_reader() returnerar en pyarrow.RecordBatchReader, ett standard Arrow-gränssnitt som många bibliotek accepterar direkt. Att anropa reader.read_all() läser in hela strömmen till en tabell.

  5. Parquet I/O: pyarrow.parquet.write_table() sparar Arrow-tabellen till en komprimerad Parquet-fil. Detta format bevarar kolumntyper och stödjer effektiva partiella läsningar.

Nästa steg

Använd dessa artiklar för att fortsätta bygga:

  • Arrow-integration för avancerade Arrow-mönster inklusive batchbearbetning, minneshantering och bibliotekskommunikation.
  • pandas-integration för att ladda frågeresultat direkt i DataFrames.
  • Polarsintegrering för att skapa Polars-dataframes från Arrow-baserade frågor.