Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
DuckDB — это процессуемый SQL-аналитический движок, который может напрямую запрашивать таблицы Apache Arrow без копирования данных. Объединение DuckDB с драйвером mssql-python позволяет вам:
- Запускайте аналитические SQL-запросы на наборах результатов Microsoft SQL без загрузки данных в pandas или Polars.
- Выполняйте запросы к таблицам Arrow в памяти без дополнительных затрат на копирование.
- Соедините данные Microsoft SQL с локальными файлами (CSV, Parquet, JSON) в одном запросе DuckDB.
- Экспортируйте данные Microsoft SQL в Parquet, CSV или другие форматы через DuckDB.
Необходимые условия
- Python 3.10 или более поздней версии.
- Пакеты
mssql-python,duckdbиpyarrow. Установите всё с помощьюpip install mssql-python duckdb pyarrow. - Установите единовременные предварительные условия для операционной системы. Пользователи Windows могут пропустить этот шаг. Для полной информации о платформе см. Установить mssql-python.
apk add libtool krb5-libs krb5-dev
Создание базы данных SQL
Создайте или подключитесь к SQL-базе данных на одной из следующих платформ:
Примеры в этой статье используют запрос к AdventureWorks примерной базе данных. Если у вас его ещё нет, посмотрите примеры баз данных AdventureWorks.
Установка зависимостей
pip install mssql-python duckdb pyarrow
Выполнение запросов к данным Microsoft SQL в DuckDB
Основной рабочий процесс: выполнить запрос с помощью mssql-python, получить результаты в виде таблицы Arrow, затем запросить эту таблицу Arrow с помощью DuckDB SQL.
Основная схема
Начните с установления соединения и получения данных в виде таблицы Arrow.
import duckdb
import mssql_python
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes"
)
cursor = conn.cursor()
# Fetch Microsoft SQL data as Arrow
cursor.execute("SELECT * FROM Production.Product WHERE ListPrice > 0")
products = cursor.arrow()
# Query the Arrow table with DuckDB
result = duckdb.sql("""
SELECT Color, COUNT(*) AS ProductCount, AVG(ListPrice) AS AvgPrice
FROM products
GROUP BY Color
ORDER BY ProductCount DESC
""")
print(result.fetchdf())
DuckDB обращается к таблице Arrow products по имени её переменной в Python. Данные не копируются в хранилище DuckDB.
Агрегат и фильтр
Используйте SQL DuckDB для группировки и агрегирования данных Arrow.
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
# Top customers by total spend
top_customers = duckdb.sql("""
SELECT
CustomerID,
COUNT(*) AS OrderCount,
SUM(TotalDue) AS TotalSpent,
AVG(TotalDue) AS AvgOrderValue
FROM orders
GROUP BY CustomerID
HAVING SUM(TotalDue) > 10000
ORDER BY TotalSpent DESC
LIMIT 20
""")
print(top_customers.fetchdf())
Присоединяйтесь к нескольким результатам Microsoft SQL
Получите несколько таблиц из Microsoft SQL и объедините их в DuckDB без написания кросс-серверного запроса.
# Fetch two tables
cursor.execute("SELECT * FROM Production.Product")
products = cursor.arrow()
cursor.execute("SELECT * FROM Production.ProductSubcategory")
subcategories = cursor.arrow()
# Join in DuckDB
result = duckdb.sql("""
SELECT
s.Name AS Subcategory,
COUNT(*) AS ProductCount,
ROUND(AVG(p.ListPrice), 2) AS AvgPrice
FROM products p
JOIN subcategories s ON p.ProductSubcategoryID = s.ProductSubcategoryID
GROUP BY s.Name
ORDER BY AvgPrice DESC
""")
print(result.fetchdf())
Соедините данные Microsoft SQL с локальными файлами
DuckDB может читать файлы CSV, Parquet и JSON нативно. Объедините данные SQL Server с локальными файлами в одном запросе.
Объединить через CSV-файл
Загрузите CSV-файл и объедините его с данными из Microsoft SQL.
import csv
from pathlib import Path
cursor.execute("SELECT CustomerID, PersonID FROM Sales.Customer")
customers = cursor.arrow()
csv_path = Path("customer_regions.csv")
with csv_path.open("w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["CustomerID", "Region", "Segment"])
writer.writerows([
(1, "West", "Premium"),
(2, "East", "Standard"),
(3, "Central", "Basic"),
])
try:
result = duckdb.sql("""
SELECT c.CustomerID, c.PersonID, f.Region, f.Segment
FROM customers c
JOIN read_csv_auto('customer_regions.csv') f ON c.CustomerID = f.CustomerID
""")
print(result.fetchdf())
finally:
csv_path.unlink(missing_ok=True)
Объединение с файлом Parquet
Загрузите файл Parquet и объедините его с данными из Microsoft SQL.
from pathlib import Path
import pyarrow as pa
import pyarrow.parquet as pq
cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product")
products = cursor.arrow()
parquet_path = Path("order_history.parquet")
order_history = pa.table({
"ProductID": [1, 2, 680],
"OrderDate": ["2024-06-01", "2024-03-15", "2024-01-10"],
"Quantity": [10, 5, 3],
})
pq.write_table(order_history, parquet_path)
try:
result = duckdb.sql("""
SELECT p.Name, p.ListPrice, h.OrderDate, h.Quantity
FROM products p
JOIN read_parquet('order_history.parquet') h ON p.ProductID = h.ProductID
WHERE h.OrderDate >= '2024-01-01'
""")
print(result.fetchdf())
finally:
parquet_path.unlink(missing_ok=True)
Экспорт данных Microsoft SQL
Используйте оператор COPY DuckDB для экспорта данных из Microsoft SQL в различные файловые форматы.
Экспорт в Parquet
Экспорт данных в формат Apache Parquet.
cursor.execute("SELECT * FROM Production.Product")
products = cursor.arrow()
duckdb.sql("COPY products TO 'products.parquet' (FORMAT PARQUET)")
Экспорт в CSV
Экспорт данных в файл значений с разделёнными запятыми:
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
duckdb.sql("COPY orders TO 'orders.csv' (FORMAT CSV, HEADER)")
Экспортный разделённый паркет
Экспорт данных в разделённые файлы Parquet для распределённой аналитики:
import shutil
from pathlib import Path
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
output_dir = Path("sales_data")
shutil.rmtree(output_dir, ignore_errors=True)
duckdb.sql("""
COPY (SELECT *, YEAR(OrderDate) AS OrderYear FROM orders)
TO 'sales_data'
(FORMAT PARQUET, PARTITION_BY (OrderYear))
""")
Потоковая передача больших наборов результатов
Для больших наборов данных используйте arrow_reader(), чтобы обрабатывать данные потоковыми пакетами, не загружая все строки в память одновременно:
cursor.execute("SELECT * FROM Production.TransactionHistory")
reader = cursor.arrow_reader(batch_size=50000)
# Process each batch with DuckDB
total_rows = 0
for batch in reader:
result = duckdb.sql("""
SELECT ProductID, SUM(ActualCost) AS TotalCost
FROM batch
GROUP BY ProductID
""")
total_rows += batch.num_rows
print(f"Processed {total_rows} rows")
Накопление результатов стриминга
Для агрегирования по всем партиям регистрируйте каждую партию в постоянном соединении DuckDB и накапливайте результаты постепенно.
cursor.execute("SELECT * FROM Production.TransactionHistory")
reader = cursor.arrow_reader(batch_size=50000)
duck = duckdb.connect()
duck.execute("CREATE TABLE transactions (ProductID INT, ActualCost DOUBLE, Quantity INT)")
for batch in reader:
duck.execute("INSERT INTO transactions SELECT ProductID, ActualCost, Quantity FROM batch")
# Query the accumulated data
result = duck.sql("""
SELECT ProductID, SUM(ActualCost) AS TotalCost, SUM(Quantity) AS TotalQty
FROM transactions
GROUP BY ProductID
ORDER BY TotalCost DESC
LIMIT 10
""")
print(result.fetchdf())
duck.close()
Советы по производительности
Пусть Microsoft SQL возьмёт на себя тяжёлую работу
Microsoft SQL быстрее подходит для фильтрации, объединений и агрегирования, чем перемещение всех сырых данных по проводу. Используйте DuckDB для вторичного анализа уже полученных наборов результатов, а не как замену оптимизации запросов SQL Server.
# Suboptimal: Pull all rows, filter in DuckDB
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
result = duckdb.sql("SELECT * FROM orders WHERE TotalDue > 1000")
# Better: Filter in Microsoft SQL, analyze in DuckDB
cursor.execute("SELECT * FROM Sales.SalesOrderHeader WHERE TotalDue > 1000")
orders = cursor.arrow()
result = duckdb.sql("SELECT CustomerID, SUM(TotalDue) FROM orders GROUP BY CustomerID")
Используйте Arrow для всех операций чтения
Передача на основе стрелок предотвращает создание промежуточных объектов Python, что снижает использование памяти и повышает пропускную способность. При передаче данных в DuckDB предпочитайте cursor.arrow() вместо ручного построчного преобразования.
Используйте потоковые потоки для больших наборов данных
Для наборов результатов, превышающих доступную память, используйте arrow_reader() с параметром batch_size для постепенной обработки данных.