您好,登录后才能下订单哦!
# Django中怎么执行原生SQL
## 前言
在Django开发中,ORM(对象关系映射)提供了强大的数据操作能力,能够满足90%以上的数据库操作需求。但在某些特殊场景下(如复杂报表查询、性能优化、数据库特性使用等),我们可能需要直接执行原生SQL语句。本文将全面介绍Django中执行原生SQL的多种方式及其最佳实践。
---
## 一、为什么需要执行原生SQL
### 1.1 ORM的局限性
虽然Django ORM功能强大,但在以下场景可能力不从心:
- 复杂多表连接查询
- 数据库特定函数(如PostgreSQL的JSON操作)
- 需要精细控制SQL执行计划
- 批量操作性能优化
### 1.2 性能考量
某些复杂查询使用原生SQL可能比ORM转换后的查询效率更高。例如:
```python
# ORM方式(可能产生N+1查询)
books = Book.objects.filter(author__name='鲁迅').select_related('author')
# 原生SQL可能更高效
"SELECT * FROM books_book INNER JOIN books_author ON books_book.author_id = books_author.id WHERE books_author.name = '鲁迅'"
最基础的原生SQL执行方式,返回模型实例:
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=100)
author = models.CharField(max_length=50)
# 执行原生查询
books = Book.objects.raw('SELECT * FROM books_book WHERE author = %s', ['鲁迅'])
for book in books:
print(book.name)
# 位置参数
Book.objects.raw('SELECT * FROM books_book WHERE id = %s', [1])
# 命名参数
Book.objects.raw('SELECT * FROM books_book WHERE id = %(id)s', {'id': 1})
需要直接访问数据库连接时使用:
from django.db import connection
def my_custom_sql():
with connection.cursor() as cursor:
# 执行查询
cursor.execute("SELECT * FROM books_book WHERE author = %s", ['鲁迅'])
# 获取结果方式1:字典格式
columns = [col[0] for col in cursor.description]
results = [dict(zip(columns, row)) for row in cursor.fetchall()]
# 获取结果方式2:原始元组
# results = cursor.fetchall()
return results
connections['别名']
)from django.db import transaction
with transaction.atomic():
with connection.cursor() as cursor:
cursor.execute("UPDATE books_book SET price = price * 1.1")
Django提供了一些便捷的游标方法:
from django.db import connection
def get_book_count():
with connection.cursor() as cursor:
cursor.execute("SELECT COUNT(*) FROM books_book")
row = cursor.fetchone()
return row[0]
fetchone()
:获取单条记录fetchmany(size)
:获取指定数量记录fetchall()
:获取所有记录executemany()
:批量执行(适合批量插入)在settings.py配置多个数据库时:
from django.db import connections
with connections['replica'].cursor() as cursor:
cursor.execute("SELECT * FROM books_book")
# 处理结果...
以MySQL为例:
with connection.cursor() as cursor:
cursor.callproc('my_stored_procedure', [param1, param2])
results = cursor.fetchall()
对于大数据量查询:
def large_query():
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM large_table")
while True:
batch = cursor.fetchmany(1000) # 每次获取1000条
if not batch:
break
process_batch(batch)
extra()
方法过渡ORM与原生SQL的折中方案:
Book.objects.extra(
where=["author = %s"],
params=['鲁迅'],
select={'lower_name': 'LOWER(name)'}
)
错误示范:
# 危险!容易导致SQL注入
query = "SELECT * FROM books_book WHERE author = '%s'" % user_input
正确做法:
# 使用参数化查询
cursor.execute("SELECT * FROM books_book WHERE author = %s", [user_input])
即使使用参数化查询,也应验证输入:
from django.core.exceptions import ValidationError
def validate_author_name(name):
if not name.isalpha(): # 简单示例
raise ValidationError("Invalid author name")
with
语句)确保连接关闭使用executemany
提高批量插入效率:
data = [(1, 'Book1'), (2, 'Book2')]
with connection.cursor() as cursor:
cursor.executemany("INSERT INTO books_book VALUES (%s, %s)", data)
对于复杂查询,可以在SQL中指定索引:
cursor.execute("SELECT * FROM books_book USE INDEX (author_index) WHERE author = %s", ['鲁迅'])
from django.test import TestCase
class SQLTests(TestCase):
def test_raw_query(self):
with self.assertNumQueries(1):
list(Book.objects.raw("SELECT * FROM books_book"))
在settings.py中启用:
LOGGING = {
'version': 1,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
}
},
'loggers': {
'django.db.backends': {
'level': 'DEBUG',
'handlers': ['console'],
}
}
}
with connection.cursor() as cursor:
cursor.execute("EXPLN ANALYZE SELECT * FROM books_book")
print(cursor.fetchall())
在转向原生SQL前,可尝试:
- select_related()
/ prefetch_related()
- annotate()
聚合
- F()
表达式
- 自定义Manager/QuerySet
django-extra-views
:增强的ORM功能django-sql-explorer
:SQL查询工具虽然Django ORM足够强大,但掌握原生SQL执行能力仍是Django开发者的重要技能。合理使用原生SQL可以在保持Django优势的同时,解决特定场景下的特殊需求。关键是要: 1. 优先考虑ORM解决方案 2. 必要时谨慎使用原生SQL 3. 始终注意安全性和性能 4. 编写清晰的文档和测试
通过本文介绍的各种方法和最佳实践,希望您能在Django项目中游刃有余地处理各种数据访问需求。 “`
注:本文实际约2650字(含代码示例),完整覆盖了Django执行原生SQL的主要技术点。根据具体需求,可适当调整各部分篇幅。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。