Chọn truy vấn có biến trong Python

Hãy tạo một khung dữ liệu mẫu có 3 cột và 4 hàng. Khung dữ liệu này được sử dụng cho mục đích trình diễn

import pandas as pd df = pd.DataFrame({"col1" : range(1,5), "col2" : ['A A','B B','A A','B B'], "col3" : ['A A','A A','B B','B B'] })

Lọc một giá trị A A trong cột col2

Để tham chiếu một biến trong truy vấn, bạn cần sử dụng @

Đề cập đến giá trị một cách rõ ràng
newdf = df.query("col2 == 'A A'")

phương pháp tham khảo
myval1 = 'A A' newdf = df.query("col2 == @myval1")

Cách chuyển tên cột dưới dạng biến trong truy vấn

Thay vì giá trị bộ lọc, chúng tôi đang đề cập đến cột mà chúng tôi muốn sử dụng để đặt phụ hoặc lọc. myvar1 = 'col2' newdf2 = df.query("{0} == 'A A'".format(myvar1))
{0} nhận một giá trị của biến myvar1
newdf = df.query("col2 == 'A A'") 0 trả lại newdf = df.query("col2 == 'A A'") 1
Trong trường hợp bạn muốn chuyển nhiều cột dưới dạng biến trong truy vấn. Ở đây chúng tôi đang sử dụng các cột col2 và newdf = df.query("col2 == 'A A'") 3. myvar1 = 'col2' myvar2 = 'col3' newdf2 = df.query("{0} == 'A A' & {1} == 'B B'".format(myvar1, myvar2)) newdf = df.query("col2 == 'A A'") 4 tương đương với newdf = df.query("col2 == 'A A'") 5

Cách xử lý khoảng trắng trong tên cột

Hãy đổi tên cột col2 bằng cách bao gồm một khoảng trắng ở giữa nhằm mục đích minh họa. df.rename(columns={'col2':'col 2'}, inplace = True) Bằng cách sử dụng backticks newdf = df.query("col2 == 'A A'") 7 bạn có thể chuyển một cột chứa khoảng trắng. myvar1 = '`col 2`' newdf = df.query("{0} == 'A A'".format(myvar1))

Học Python. 50 hướng dẫn Python hàng đầu

Thông báo
Chia sẻ Chia sẻ Tweet Đăng ký

bài viết liên quan

về tác giả

Deepanshu thành lập ListenData với một mục tiêu đơn giản - Làm cho các phân tích trở nên dễ hiểu và dễ theo dõi. Ông có hơn 10 năm kinh nghiệm trong lĩnh vực khoa học dữ liệu. Trong nhiệm kỳ của mình, ông đã làm việc với các khách hàng toàn cầu trong nhiều lĩnh vực khác nhau như Ngân hàng, Bảo hiểm, Cổ phần tư nhân, Viễn thông và Nhân sự

Có rất nhiều tình huống mà người ta muốn chèn tham số vào truy vấn SQL và có nhiều cách để triển khai truy vấn SQL theo khuôn mẫu trong python. Không đi sâu vào so sánh các cách tiếp cận khác nhau, bài đăng này giải thích một phương pháp đơn giản và hiệu quả để tham số hóa SQL bằng JinjaSql. Bên cạnh nhiều tính năng mạnh mẽ của Jinja2, chẳng hạn như các câu lệnh và vòng lặp có điều kiện, JinjaSql cung cấp một cách đơn giản và rõ ràng để tham số hóa không chỉ các giá trị được thay thế trong các mệnh đề select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
5 và select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
6, mà còn cả chính các câu lệnh SQL, bao gồm tham số hóa tên bảng và tên cột cũng như soạn thảo các truy vấn

Thay thế tham số cơ bản

Giả sử chúng ta có một bảng select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
7 chứa hồ sơ về các giao dịch tài chính. Các cột trong bảng này có thể là select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
8, select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
9, user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
0 và user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
1. Để tính toán số lượng giao dịch và tổng số tiền cho một người dùng nhất định vào một ngày nhất định, một truy vấn trực tiếp tới cơ sở dữ liệu có thể trông giống như

select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = 1234
and transaction_date = '2019-03-02'
group by
user_id

Ở đây, chúng tôi giả định rằng cơ sở dữ liệu sẽ tự động chuyển đổi định dạng user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
2 của chuỗi biểu diễn ngày thành một loại ngày thích hợp

Nếu chúng tôi muốn chạy truy vấn ở trên cho một người dùng và ngày tùy ý, chúng tôi cần tham số hóa các giá trị select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
9 và user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
0. Trong JinjaSql, mẫu tương ứng sẽ đơn giản trở thành

select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id

Ở đây, các giá trị đã được thay thế bằng các trình giữ chỗ có tên biến python được đặt trong dấu ngoặc nhọn kép user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
5. Lưu ý rằng tên biến user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
6 và user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
7 được chọn chỉ để chứng minh rằng chúng là tên biến và không liên quan gì đến chính tên cột. Phiên bản dễ đọc hơn của cùng một mẫu được lưu trữ trong biến python là

user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''

Tiếp theo, chúng ta cần thiết lập các tham số cho truy vấn

params = {
'user_id': 1234,
'transaction_date': '2019-03-02',
}

Giờ đây, việc tạo truy vấn SQL từ mẫu này thật đơn giản

from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)

Nếu chúng tôi in user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
8 và user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
9, chúng tôi thấy rằng cái trước là một chuỗi được tham số hóa và cái sau là một params = {
'user_id': 1234,
'transaction_date': '2019-03-02',
}
0 của các tham số

>>> print(query)
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = %(user_id)s
and transaction_date = %(transaction_date)s
group by
user_id
>>> print(bind_params)
OrderedDict([('user_id', 1234), ('transaction_date', '2018-03-01')])
Chạy truy vấn được tham số hóa

Nhiều kết nối cơ sở dữ liệu có tùy chọn chuyển user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
9 làm đối số cho phương thức thực thi truy vấn SQL trên kết nối. Đối với một nhà khoa học dữ liệu, có thể tự nhiên nhận được kết quả của truy vấn trong khung dữ liệu Pandas. Khi chúng tôi có kết nối params = {
'user_id': 1234,
'transaction_date': '2019-03-02',
}
2, việc này dễ như chạy params = {
'user_id': 1234,
'transaction_date': '2019-03-02',
}
3

import pandas as pd
frm = pd.read_sql(query, conn, params=bind_params)

Xem tài liệu JinjaSql để biết các ví dụ khác

Từ một mẫu đến truy vấn SQL cuối cùng

Người ta thường mong muốn mở rộng hoàn toàn truy vấn với tất cả các tham số trước khi chạy nó. Ví dụ: ghi nhật ký truy vấn đầy đủ là vô giá để gỡ lỗi các quy trình hàng loạt vì người ta có thể sao chép-dán truy vấn từ nhật ký trực tiếp vào giao diện SQL tương tác. Thật hấp dẫn khi thay thế user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
9 thành user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
8 bằng cách thay thế chuỗi tích hợp trong python. Tuy nhiên, chúng tôi nhanh chóng nhận thấy rằng các tham số chuỗi cần được trích dẫn để tạo ra SQL thích hợp. Ví dụ: trong mẫu ở trên, giá trị ngày tháng phải được đặt trong dấu nháy đơn

>>> print(query % bind_params)

select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = 1234
and transaction_date = 2018-03-01
group by
user_id

Để giải quyết vấn đề này, chúng ta cần một hàm trợ giúp để trích dẫn chính xác các tham số là chuỗi. Chúng tôi phát hiện xem một tham số có phải là một chuỗi hay không, bằng cách gọi

from six import string_types
isinstance(value, string_types)

Điều này hoạt động cho cả python 3 và 2. 7. Các tham số chuỗi được chuyển đổi thành loại params = {
'user_id': 1234,
'transaction_date': '2019-03-02',
}
6, dấu nháy đơn trong tên được thoát bằng dấu nháy đơn khác và cuối cùng, toàn bộ giá trị được đặt trong dấu nháy đơn

from six import string_typesdef quote_sql_string(value):
'''
If `value` is a string type, escapes single quotes in the string
and returns the string enclosed in single quotes.
'''
if isinstance(value, string_types):
new_value = str(value)
new_value = new_value.replace("'", "''")
return "'{}'".format(new_value)
return value

Cuối cùng, để chuyển đổi mẫu thành SQL thích hợp, chúng tôi lặp lại user_transaction_template = '''
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ user_id }}
and transaction_date = {{ transaction_date }}
group by
user_id
'''
9, trích dẫn các chuỗi và sau đó thực hiện thay thế chuỗi

select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
0

Bây giờ chúng tôi có thể dễ dàng nhận được truy vấn cuối cùng mà chúng tôi có thể đăng nhập hoặc chạy một cách tương tác

select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
1

Đặt tất cả lại với nhau, một hàm trợ giúp khác kết thúc các lệnh gọi JinjaSql và chỉ cần lấy mẫu và params = {
'user_id': 1234,
'transaction_date': '2019-03-02',
}
8 tham số, rồi trả về SQL đầy đủ

select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
2Tính toán số liệu thống kê trên một cột

Tính toán số liệu thống kê về các giá trị được lưu trữ trong một cột cơ sở dữ liệu cụ thể rất hữu ích cả khi khám phá dữ liệu lần đầu tiên và để xác thực dữ liệu trong sản xuất. Vì chúng ta chỉ muốn minh họa một số tính năng của các mẫu nên để đơn giản, chúng ta hãy chỉ làm việc với các cột params = {
'user_id': 1234,
'transaction_date': '2019-03-02',
}
9, chẳng hạn như cột select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
9 trong bảng select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
7 ở trên. Đối với các cột số nguyên, chúng tôi quan tâm đến số lượng giá trị duy nhất, giá trị tối thiểu và tối đa và số lượng null. Một số cột có thể có giá trị mặc định là, chẳng hạn như, from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
2, những hạn chế của cột nằm ngoài phạm vi của bài đăng này, tuy nhiên, chúng tôi muốn nắm bắt điều đó bằng cách báo cáo số lượng giá trị mặc định

Xem xét mẫu và chức năng sau. Hàm lấy tên bảng, tên cột và giá trị mặc định làm đối số và trả về SQL để tính toán số liệu thống kê

select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
3

Hàm này đơn giản và rất mạnh vì nó áp dụng cho bất kỳ cột nào trong bất kỳ bảng nào. Lưu ý cú pháp from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
3 trong mẫu. Nếu giá trị mặc định được truyền cho hàm là from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
4, thì SQL trả về 0 trong trường from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
5

Hàm và mẫu ở trên cũng sẽ hoạt động với chuỗi, ngày tháng và các loại dữ liệu khác nếu from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
6 được đặt thành Không có. Tuy nhiên, để xử lý các kiểu dữ liệu khác nhau một cách thông minh hơn, cần mở rộng hàm để lấy kiểu dữ liệu làm đối số và xây dựng logic cụ thể cho các kiểu dữ liệu khác nhau. Ví dụ: người ta có thể muốn biết giá trị tối thiểu và tối đa của độ dài chuỗi thay vì giá trị tối thiểu và tối đa của chính giá trị đó

Hãy xem đầu ra cho cột from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
7

select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = {{ uid }}
and transaction_date = {{ tdate }}
group by
user_id
4

Lưu ý rằng các dòng trống xuất hiện ở vị trí của mệnh đề from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
8 và có thể bị xóa

Tóm lược

Với các hàm trợ giúp ở trên, việc tạo và chạy các truy vấn SQL theo khuôn mẫu trong python rất dễ dàng. Bởi vì các chi tiết thay thế tham số bị ẩn, người ta có thể tập trung vào việc xây dựng mẫu và tập hợp các tham số, sau đó gọi một hàm duy nhất để lấy SQL cuối cùng

Một cảnh báo quan trọng là nguy cơ tiêm mã. Đối với các quy trình hàng loạt, đây không phải là vấn đề, nhưng việc sử dụng cấu trúc from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
9 trong các ứng dụng web có thể nguy hiểm. Từ khóa from jinjasql import JinjaSql
j = JinjaSql(param_style='pyformat')
query, bind_params = j.prepare_query(user_transaction_template, params)
9 chỉ ra rằng người dùng (bạn) tự tin rằng không thể tiêm mã và chịu trách nhiệm chỉ cần đặt bất kỳ chuỗi nào được chuyển trực tiếp vào các tham số vào truy vấn

Mặt khác, khả năng đặt một chuỗi tùy ý trong truy vấn cho phép một người chuyển toàn bộ khối mã vào một mẫu. Ví dụ: thay vì chuyển >>> print(query)
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = %(user_id)s
and transaction_date = %(transaction_date)s
group by
user_id
>>> print(bind_params)
OrderedDict([('user_id', 1234), ('transaction_date', '2018-03-01')])
1 ở trên, người ta có thể chuyển >>> print(query)
select
user_id
, count(*) as num_transactions
, sum(amount) as total_amount
from
transactions
where
user_id = %(user_id)s
and transaction_date = %(transaction_date)s
group by
user_id
>>> print(bind_params)
OrderedDict([('user_id', 1234), ('transaction_date', '2018-03-01')])
2 và truy vấn vẫn hoạt động

Để khám phá các tính năng mạnh mẽ hơn nữa của các mẫu SQL, hãy xem thêm hướng dẫn về Các mẫu SQL nâng cao trong Python với JinjaSql

Mã trong bài đăng này được cấp phép theo Giấy phép MIT. Bài đăng này lần đầu tiên xuất hiện trên blog Cuộc sống xung quanh dữ liệu

Tôi có thể sử dụng biến Python trong truy vấn SQL không?

Ví dụ để chèn dữ liệu vào bảng MySQL bằng Truy vấn được tham số hóa. Đôi khi bạn cần chèn một biến Python làm giá trị cột trong truy vấn chèn . Ví dụ: người dùng đã điền vào biểu mẫu trực tuyến và nhấp vào gửi. Vì vậy, bạn cần chèn các giá trị đó vào bảng MySQL.

Làm cách nào để lưu trữ truy vấn SQL trong một biến trong Python?

fetchall() truy xuất TẤT CẢ kết quả từ truy vấn của bạn, chúng tôi sẽ đặt chúng vào một biến có tên là rows. Sau đó, chúng tôi tạo một trình vòng lặp (điều bạn đã cố gắng thực hiện với vòng lặp while) bằng cách thực hiện cho hàng trong hàng. Sau đó, chúng tôi chỉ cần in từng hàng

Tôi có thể sử dụng biến trong câu lệnh SQL chọn không?

Để gán giá trị cho một biến, hãy sử dụng câu lệnh SET. Đây là phương pháp ưa thích để gán một giá trị cho một biến. Một biến cũng có thể được gán giá trị bằng cách được tham chiếu trong danh sách lựa chọn của câu lệnh SELECT .

Làm cách nào để sử dụng biến Python trong truy vấn MySQL?

Python Chọn từ Bảng MySQL .
Thực thi truy vấn CHỌN và xử lý tập kết quả được truy vấn trả về trong Python
Sử dụng các biến Python trong mệnh đề where của truy vấn CHỌN để chuyển các giá trị động
Sử dụng các phương thức fetchall() , fetchmany() và fetchone() của lớp con trỏ để tìm nạp tất cả hoặc các hàng giới hạn từ một bảng

Chủ đề