2016-05-05 21 views
6

SQL Server'da bir aralığın bu aralıkta olup olmadığını kontrol etmek için bir aralık gerektiren bir sorgu kullanıyorum (örneğin DemographicGroupDimID olup olmadığını kontrol etmek için aşağıdaki gibi) . ya (1,2 veya 3) bunu yapabilmek için bulunan tek yolu googling bazı yaptıktan sonra altındaydı:Paramler ile SQL Server'dan okuma: düzgün çalışmayan pandalar (veya pyodbc)

SQL dinamik farklı olan, bunu yapmak gerekirse

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] 

aralıklar bu şekilde başarısız olur:

Pandalar sorgusu

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)']) 

hatası: bir bildirmek select deyimi kendisi sınırları içinde olması gerekir çünkü

--------------------------------------------------------------------------- 
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)") 

bu mudur? pandaspyodbc imleç nesnesini nasıl işlediğinden emin değilim, bu nedenle bu hatanın nereden kaynaklandığından emin değilim.

Düzenleme: Sadece not etmek gerekirse, bu örnekte geçirdiğim param, yalnızca başarısız olan aralıkta yalnızca bir sayı olduğunda basit durumu kullanmak için (30) idi. Tabii ki yukarıdaki örnekte olduğu gibi (1), (2), (3) gibi daha karmaşık dizeler için de başarısız oluyor.

+0

ek bir bilgi: nihayet SQL arayın. Bu yüzden belki de bir çözüm, SQL sorgu dizesinin kendisini dinamik olarak biçimlendirmek olabilir. – mburke05

+0

İki şey: 1) SQL'in 'IN (?,?,?)' Fıkralarını bir avuç dolusu tamsayı skaler geçmeden kullanamazsınız; veya 2) neden parazitlerden geçen saklı bir prosedürü çağırmıyorsunuz? – Parfait

cevap

5

SQL'inizde prepared statements kullanırsanız, bir placeholder/parameter/bind değişkenine birden çok değer koyamazsınız! Yalnızca literals yerine yer tutucular/parametreler/bağlama değişkenleri kullanabilirsiniz Bunun yanında

, sen değil hazır bilgi SQL sorgusunun parçası için kullanamaz.

Durumda da ( ve SQL parçasıdır ) ancak değil parametreleri olarak bir değişmez koymak için çalıştı.

Parametreleri/hazırlanmış deyimleri/bağlama değişkenini kullanmak sizi bazı SQL enjeksiyonlarından da koruyacaktır.aşağıdaki gibi

söyledi kodunuzu değiştirmeyi deneyin:

INSERT INTO @adults VALUES ? 

INSERT INTO @adults VALUES (?) 

ve

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

için

için

değişiklik

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

GÜNCELLEME:

bu şekilde sizin SQL hazırlayabilirsiniz

: o zaman

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

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

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

: at

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' 

dikkat edin oluşturulan

(?) (?) (?) ve param gerçek değeri değiştirilmesi durumunda bu sorgu çalışıyor:

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

ama bu sadece tek bir değere sahip olduğum durumda çalışıyor? Mesela 4 değeri geçmek gerekiyorsa [1,2,3,4]? "sql_" değerini "VALUES (?), (?), (?), (?)" olarak değiştirmek zorunda mıyım? Ayrıca son iki cümlesini takip etmiyorum (ya da belki de 'VALUES' ifadesinin rolünü yanlış anladım), bir dize, bir edebi olmadığını ima ederek, "sadece edebi değerler için" ne demek istediğini açıklayabilir miydiniz? Tekrar teşekkürler! – mburke05

+0

teşekkürler, evet, işte yapılması gerekenleri anladım. ama belki de param hatalarını mı öldürdüyse ya da pandalarda bir hata olsaydı emin değildim. – mburke05

+0

Cevabınızı max kabul ettim, ama soru hala kalır, pandalar/pyodbc beklenen bu davranış veya bu bir hatadır. – mburke05

İlgili konular