2016-05-05 20 views
6

Tôi đang sử dụng truy vấn trong SQL Server yêu cầu phạm vi kiểm tra xem số có nằm trong phạm vi đó hay không (ví dụ như dưới đây để kiểm tra xem DemographicGroupDimID là . hoặc (1,2 hoặc 3) Sau khi thực hiện một số googling cách duy nhất tôi tìm thấy để có thể làm điều đó là dưới đây:Đọc từ SQL Server với tham số: gấu trúc (hoặc pyodbc) không hoạt động đúng

SQL

DECLARE @adults table (Id int) 
INSERT INTO @adults VALUES (1), (2), (3) 

SELECT [date], [station], [impression] = SUM([impressions])/COUNT(DISTINCT [datetime]) 
     FROM 
     (SELECT [datetime] = DATEADD(minute,td.Minute,DATEADD(hour,td.NielsenLocalHour,CONVERT(smalldatetime, ddt.DateKey))), [date] = ddt.DateKey, [station] = nd.Name, [impressions] = SUM(naf.Impression) 
     FROM [Nielsen].[dbo].[NielsenAnalyticsFact] as naf 
     LEFT JOIN [dbo].[DateDim] AS ddt 
     ON naf.StartDateDimID = ddt.DateDimID 
     LEFT JOIN [dbo].NetworkDim as nd 
     ON naf.NetworkDimID = nd.NetworkDimID 
     LEFT JOIN [dbo].TimeDim as td 
     ON naf.QuarterHourDimID = td.TimeDimID 
     WHERE (naf.NielsenMarketDimID = 1 
        AND naf.RecordTypeDimID = 2 
        AND naf.AudienceEstimateTypeDimID = 1 
        AND naf.DailyOrWeeklyDimID = 1 
        AND naf.RecordSequenceCodeDimID = 5 
        AND naf.ViewingTypeDimID = 4 
        AND naf.QuarterHourDimID IS NOT NULL 
        AND naf.DemographicGroupDimID < 31 
        AND nd.Affiliation = 'Cable' 
        AND naf.NetworkDimID != 1278 
        AND naf.DemographicGroupDimID in (SELECT Id FROM @adults)) 
     GROUP BY DATEADD(minute,td.Minute,DATEADD(hour,td.NielsenLocalHour,CONVERT(smalldatetime, ddt.DateKey))), nd.Name, ddt.DateKey) 
AS grouped_table 
GROUP BY [date], [station] 
ORDER BY [date], [station] 

Nếu tôi cần phải tự động làm điều này, với khác nhau phạm vi, điều này không thành công, như vậy:

Truy vấn gấu trúc

from queries import DB_CREDENTIALS 
import pyodbc 
import pandas as pd 

sql_ = """DECLARE @adults table (Id int) 
INSERT INTO @adults VALUES ? 

SELECT [date], [station], [impression] = SUM([impressions])/COUNT(DISTINCT [datetime]) 
     FROM 
     (SELECT [datetime] = DATEADD(minute,td.Minute,DATEADD(hour,td.NielsenLocalHour,CONVERT(smalldatetime, ddt.DateKey))), [date] = ddt.DateKey, [station] = nd.Name, [impressions] = SUM(naf.Impression) 
     FROM [Nielsen].[dbo].[NielsenAnalyticsFact] as naf 
     LEFT JOIN [dbo].[DateDim] AS ddt 
     ON naf.StartDateDimID = ddt.DateDimID 
     LEFT JOIN [dbo].NetworkDim as nd 
     ON naf.NetworkDimID = nd.NetworkDimID 
     LEFT JOIN [dbo].TimeDim as td 
     ON naf.QuarterHourDimID = td.TimeDimID 
     WHERE (naf.NielsenMarketDimID = 1 
        AND naf.RecordTypeDimID = 2 
        AND naf.AudienceEstimateTypeDimID = 1 
        AND naf.DailyOrWeeklyDimID = 1 
        AND naf.RecordSequenceCodeDimID = 5 
        AND naf.ViewingTypeDimID = 4 
        AND naf.QuarterHourDimID IS NOT NULL 
        AND naf.DemographicGroupDimID < 31 
        AND nd.Affiliation = 'Cable' 
        AND naf.NetworkDimID != 1278 
        AND naf.DemographicGroupDimID in (SELECT Id FROM @adults)) 
     GROUP BY DATEADD(minute,td.Minute,DATEADD(hour,td.NielsenLocalHour,CONVERT(smalldatetime, ddt.DateKey))), nd.Name, ddt.DateKey) 
AS grouped_table 
GROUP BY [date], [station] 
ORDER BY [date], [station]""" 

with pyodbc.connect(DB_CREDENTIALS) as cnxn: 
    df = pd.read_sql(sql=sql_, con=cnxn, params=['(30)']) 

Lỗi:

--------------------------------------------------------------------------- 
DatabaseError        Traceback (most recent call last) 
<ipython-input-5-4b63847d007f> in <module>() 
     1 with pyodbc.connect(DB_CREDENTIALS) as cnxn: 
----> 2  df = pd.read_sql(sql=sql_, con=cnxn, params=['(30)']) 

C:\Users\mburke\AppData\Local\Continuum\Anaconda64\lib\site-packages\pandas\io\sql.pyc in read_sql(sql, con, index_col, coerce_float, params, parse_dates, columns, chunksize) 
    497    sql, index_col=index_col, params=params, 
    498    coerce_float=coerce_float, parse_dates=parse_dates, 
--> 499    chunksize=chunksize) 
    500 
    501  try: 

C:\Users\mburke\AppData\Local\Continuum\Anaconda64\lib\site-packages\pandas\io\sql.pyc in read_query(self, sql, index_col, coerce_float, params, parse_dates, chunksize) 
    1593 
    1594   args = _convert_params(sql, params) 
-> 1595   cursor = self.execute(*args) 
    1596   columns = [col_desc[0] for col_desc in cursor.description] 
    1597 

C:\Users\mburke\AppData\Local\Continuum\Anaconda64\lib\site-packages\pandas\io\sql.pyc in execute(self, *args, **kwargs) 
    1570    ex = DatabaseError(
    1571     "Execution failed on sql '%s': %s" % (args[0], exc)) 
-> 1572    raise_with_traceback(ex) 
    1573 
    1574  @staticmethod 

C:\Users\mburke\AppData\Local\Continuum\Anaconda64\lib\site-packages\pandas\io\sql.pyc in execute(self, *args, **kwargs) 
    1558     cur.execute(*args, **kwargs) 
    1559    else: 
-> 1560     cur.execute(*args) 
    1561    return cur 
    1562   except Exception as exc: 

DatabaseError: Execution failed on sql 'DECLARE @adults table (Id int) 
INSERT INTO @adults VALUES ? 

SELECT [date], [station], [impression] = SUM([impressions])/COUNT(DISTINCT [datetime]) 
     FROM 
     (SELECT [datetime] = DATEADD(minute,td.Minute,DATEADD(hour,td.NielsenLocalHour,CONVERT(smalldatetime, ddt.DateKey))), [date] = ddt.DateKey, [station] = nd.Name, [impressions] = SUM(naf.Impression) 
     FROM [Nielsen].[dbo].[NielsenAnalyticsFact] as naf 
     LEFT JOIN [dbo].[DateDim] AS ddt 
     ON naf.StartDateDimID = ddt.DateDimID 
     LEFT JOIN [dbo].NetworkDim as nd 
     ON naf.NetworkDimID = nd.NetworkDimID 
     LEFT JOIN [dbo].TimeDim as td 
     ON naf.QuarterHourDimID = td.TimeDimID 
     WHERE (naf.NielsenMarketDimID = 1 
        AND naf.RecordTypeDimID = 2 
        AND naf.AudienceEstimateTypeDimID = 1 
        AND naf.DailyOrWeeklyDimID = 1 
        AND naf.RecordSequenceCodeDimID = 5 
        AND naf.ViewingTypeDimID = 4 
        AND naf.QuarterHourDimID IS NOT NULL 
        AND naf.DemographicGroupDimID < 31 
        AND nd.Affiliation = 'Cable' 
        AND naf.NetworkDimID != 1278 
        AND naf.DemographicGroupDimID in (SELECT Id FROM @adults)) 
     GROUP BY DATEADD(minute,td.Minute,DATEADD(hour,td.NielsenLocalHour,CONVERT(smalldatetime, ddt.DateKey))), nd.Name, ddt.DateKey) 
AS grouped_table 
GROUP BY [date], [station] 
ORDER BY [date], [station]': ('42000', "[42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Incorrect syntax near '@P1'. (102) (SQLExecDirectW); [42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Statement(s) could not be prepared. (8180)") 

Đây có phải là vì báo cáo kết quả kê khai cần phải nằm trong giới hạn của báo cáo kết quả lựa chọn riêng của mình? Tôi không chắc chắn cách pandas xử lý đối tượng con trỏ pyodbc vì vậy tôi không chắc chắn lỗi này xuất phát từ đâu.

Chỉnh sửa: Chỉ cần lưu ý, thông số tôi đã chuyển trong trường hợp này là (30) chỉ để sử dụng trường hợp đơn giản khi chỉ có một số trong phạm vi không thành công. Tất nhiên cũng không thành công đối với các chuỗi phức tạp hơn như (1), (2), (3) như trường hợp với ví dụ trên.

+0

Lưu ý bổ sung: truy vấn này hoạt động tốt nếu tham số được thay thế bằng giá trị thực. Vì vậy, có lẽ một cách giải quyết sẽ chỉ định dạng tự động chuỗi truy vấn SQL. – mburke05

+0

Hai điều: 1) tại sao không sử dụng mệnh đề 'IN (?,?,?)' Của SQL nếu truyền một số vô hướng số nguyên; hoặc 2) tại sao không gọi một thủ tục lưu trữ đi qua trong params? – Parfait

Trả lời

5

Nếu bạn sử dụng prepared statements trong SQL của mình, bạn không thể đặt nhiều giá trị cho một biến giữ chỗ/tham số/ràng buộc!

Bên cạnh đó bạn có thể sử dụng placeholders/thông số/biến ràng buộc duy nhất ở vị trí của literals, bạn không thể sử dụng nó cho một phần của câu lệnh SQL đó là không một chữ.

Trong trường hợp của bạn, bạn đã cố gắng đặt () là một phần của SQL, nhưng không phải là một tham số theo nghĩa đen.

Sử dụng thông số/câu lệnh chuẩn bị/biến liên kết cũng sẽ bảo vệ bạn khỏi một số lần tiêm SQL.

mà nói, cố gắng thay đổi mã của bạn như sau:

thay đổi

INSERT INTO @adults VALUES ? 

để

INSERT INTO @adults VALUES (?) 

df = pd.read_sql(sql=sql_, con=cnxn, params=['(30)']) 

để

df = pd.read_sql(sql=sql_, con=cnxn, params=['30']) 

UPDATE:

bạn có thể chuẩn bị SQL của bạn theo cách này:

In [9]: vals = [20,30,40] 

In [32]: vals 
Out[32]: [20, 30, 40] 

In [33]: ' (?)' * len(vals) 
Out[33]: ' (?) (?) (?)' 

sau đó: sự chú ý

In [14]: sql_ = """DECLARE @adults table (Id int) 
    ....: INSERT INTO @adults VALUES {} 
    ....: 
    ....: SELECT [date], 
    ....: """ 

In [15]: sql_.format(' (?)' * len(vals)) 
Out[15]: 'DECLARE @adults table (Id int)\nINSERT INTO @adults VALUES (?) (?) (?)\n\nSELECT [date],\n' 

Pay tại tạo (?) (?) (?)

và cuối cùng gọi cho SQL của bạn:

df = pd.read_sql(sql=sql_.format(' (?)' * len(vals)), con=cnxn, params=vals) 
+0

nhưng điều này chỉ hoạt động trong trường hợp tôi có một giá trị đúng không? ví dụ nếu tôi cần phải vượt qua 4 giá trị, [1,2,3,4]? tôi có cần phải thay đổi 'sql_' thành' VALUES (?), (?), (?), (?) '? tôi cũng không làm theo hai câu cuối cùng của bạn (hoặc có lẽ hiểu lầm vai trò của câu lệnh 'VALUES') bạn có thể giải thích ý của bạn bằng" chỉ cho chữ ", ngụ ý một chuỗi không phải là chữ? cảm ơn một lần nữa! – mburke05

+0

thanks max, yeah, đó là những gì tôi figured đã được thực hiện. nhưng tôi đã không chắc chắn nếu có lẽ tôi đã lạm dụng params hoặc nếu nó là một lỗi trong gấu trúc. – mburke05

+0

Tôi đã chấp nhận câu trả lời của bạn tối đa, nhưng tôi nghĩ rằng câu hỏi vẫn còn, là hành vi này dự kiến ​​trong gấu trúc/pyodbc hoặc là một lỗi này. – mburke05

Các vấn đề liên quan