आउटपुट pyodbc कर्सर पायथन शब्दकोश के रूप में परिणाम देता है


83

मैं पायथन डिक्शनरी के रूप में pyodbc कर्सर आउटपुट (से .fetchone, .fetchmanyया .fetchall) को कैसे अनुक्रमित करूं ?

मैं बोतल का उपयोग कर रहा हूं और उसे वापस लौटने की जरूरत है ताकि वह इसे JSON के रूप में वापस कर सके।


और हां, मैंने नोटिस किया था कि यह PEPE249 के लिए अक्सर पूछे जाने वाले प्रश्न में था , हालांकि इससे मेरी आवश्यकता नहीं बदलती।
फू स्टैक

जवाबों:


163

यदि आप समय से पहले कॉलम नहीं जानते हैं, तो शब्दकोशों की सूची बनाने के लिए कॉलम नामों की सूची बनाने के लिए Cursor.description का उपयोग करें और प्रत्येक पंक्ति के साथ ज़िप करें। उदाहरण मानता है कि कनेक्शन और क्वेरी निर्मित हैं:

>>> cursor = connection.cursor().execute(sql)
>>> columns = [column[0] for column in cursor.description]
>>> print(columns)
['name', 'create_date']
>>> results = []
>>> for row in cursor.fetchall():
...     results.append(dict(zip(columns, row)))
...
>>> print(results)
[{'create_date': datetime.datetime(2003, 4, 8, 9, 13, 36, 390000), 'name': u'master'},   
 {'create_date': datetime.datetime(2013, 1, 30, 12, 31, 40, 340000), 'name': u'tempdb'},
 {'create_date': datetime.datetime(2003, 4, 8, 9, 13, 36, 390000), 'name': u'model'},     
 {'create_date': datetime.datetime(2010, 4, 2, 17, 35, 8, 970000), 'name': u'msdb'}]

1
के बारे में नहीं पता था cursor.description। इसने मुझे समय की एक नाव को बचाया।
तेहरिस

काम करने के लिए प्रिंट (कॉलम) और प्रिंट (परिणाम) के चारों ओर कोष्ठक को लपेटने की आवश्यकता है
LJT

2
@LJT केवल python3 में ... लेकिन चूंकि python2 में प्रिंट () फ़ंक्शन काम करता है, इसलिए इसका उपयोग करना अच्छा है।
शुभ अंक

1
@BenLutgens क्योंकि उदाहरण डिक्ट्स की एक सूची बनाता है , न कि एक तानाशाही।
शुभ अंक

1
अपडेट: डिफ़ॉल्ट रूप से, pypyodbc लोअरकेस = True सेट करता है। आप इसे इस तरह से ओवरराइट कर सकते हैं: pypyodbc आयात करें; pypyodbc.lowercase = गलत। संदर्भ: लिंक
वीहुई गुओ

13

बॉटलफी के साथ @ बर्गल के परिणाम का उपयोग करते हुए, मैं समापन बिंदु को उजागर करते हुए यह बहुत संक्षिप्त प्रश्न बनाने में सक्षम था:

@route('/api/query/<query_str>')
def query(query_str):
    cursor.execute(query_str)
    return {'results':
            [dict(zip([column[0] for column in cursor.description], row))
             for row in cursor.fetchall()]}

3
यह SQL इंजेक्शन हमलों के संपर्क में है?
बेन

@ हाँ! आपको कभी भी इसका उपयोग नहीं करना चाहिए जब तक कि आप 1000% सुनिश्चित न हों कि अनुरोध हमेशा विश्वसनीय ग्राहक से आएंगे।
बोरा एम। अल्पर

6

यहाँ एक संक्षिप्त रूप संस्करण है जिसका आप उपयोग कर सकते हैं

>>> cursor.select("<your SQL here>")
>>> single_row = dict(zip(zip(*cursor.description)[0], cursor.fetchone()))
>>> multiple_rows = [dict(zip(zip(*cursor.description)[0], row)) for row in cursor.fetchall()]

जैसा कि आप जानते हैं कि जब आप एक सूची में * जोड़ते हैं, तो आप मूल रूप से सूची को हटा देते हैं, व्यक्तिगत सूची प्रविष्टियों को उस फ़ंक्शन के मापदंडों के रूप में छोड़ देते हैं जिसे आप कॉल कर रहे हैं। जिप का उपयोग करके हम 1 से n एंट्री लेते हैं और उन्हें आप की पैंट में जिपर की तरह एक साथ जिप करते हैं।

का उपयोग करके

zip(*[(a,1,2),(b,1,2)])
# interpreted by python as zip((a,1,2),(b,1,2))

आपको मिला

[('a', 'b'), (1, 1), (2, 2)]

चूंकि विवरण टुपल्स के साथ एक ट्यूपल है, जहां प्रत्येक टपल हेडर और प्रत्येक कॉलम के लिए डेटा प्रकार का वर्णन करता है, आप प्रत्येक टपल के पहले को निकाल सकते हैं

>>> columns = zip(*cursor.description)[0]

के बराबर

>>> columns = [column[0] for column in cursor.description]

Python3.4 के साथ मुझे मिलता है: TypeError: 'zip' object is not subscriptableइसलिए मैं zip(*description)[0]ट्रिक का उपयोग नहीं कर सकता ।
मलत

अजगर 3.4 में, ज़िप एक पुनरावृत्त है। आप ज़िप को एक सूची सूची (ज़िप (* विवरण)) में लपेट सकते हैं [०] @मलत
टॉमी स्ट्रैंड

आपने columnsचर के साथ एक पंक्ति को बचाया , लेकिन प्रत्येक पंक्ति के लिए अलग से कॉलम के नामों की गणना करके फ़ंक्शन की जटिलता को गुणा किया
सर्गेई न्यूडनोव

3

मुख्य रूप से @Torxed प्रतिक्रिया से दूर होने पर, मैंने स्कीमा और डेटा को एक शब्दकोश में खोजने के लिए फ़ंक्शन का एक सामान्यीकृत सेट बनाया:

def schema_dict(cursor):
    cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';")
    schema = {}

    for it in cursor.fetchall():
        if it[0] not in schema:
            schema[it[0]]={'scheme':[]}
        else:
            schema[it[0]]['scheme'].append(it[1])

    return schema


def populate_dict(cursor, schema):
    for i in schema.keys():
        cursor.execute("select * from {table};".format(table=i))

        for row in cursor.fetchall():
            colindex = 0

            for col in schema[i]['scheme']:
                if not 'data' in schema[i]:
                    schema[i]['data']=[]

                schema[i]['data'].append(row[colindex])
                colindex += 1

    return schema

def database_to_dict():
    cursor = connect()
    schema = populate_dict(cursor, schema_dict(cursor))

लाइनों को कम करने के लिए इस पर सभी कोड-गोल्फ जाने के लिए स्वतंत्र महसूस करें; लेकिन इस बीच, यह काम करता है!

;)


2

उन स्थितियों के लिए जहां कर्सर उपलब्ध नहीं है - उदाहरण के लिए, जब पंक्तियों को किसी फ़ंक्शन कॉल या आंतरिक विधि द्वारा वापस किया गया है, तब भी आप row.cursor_description का उपयोग करके एक शब्दकोश प्रतिनिधित्व बना सकते हैं।

def row_to_dict(row):
    return dict(zip([t[0] for t in row.cursor_description], row))

1

मुझे पता है कि यह सवाल पुराना है, लेकिन इससे मुझे यह पता लगाने में मदद मिली कि मुझे क्या करने की ज़रूरत है, जो ओपी के लिए पूछ रहा था की तुलना में थोड़ा अलग है, इसलिए मैंने सोचा कि मैं साझा करूँगा, किसी और की मदद करने के लिए जो मुझे चाहिए: अगर आप SQL रूटीन क्वेरीज़ को निष्पादित करने वाले एक रूटीन को पूरी तरह से सामान्य करना चाहते हैं, लेकिन आपको एक इंडेक्स नंबर द्वारा परिणामों को संदर्भित करने की आवश्यकता है, एक नाम नहीं, आप एक शब्दकोश के बजाय सूचियों की सूची के साथ ऐसा कर सकते हैं। लौटे डेटा की प्रत्येक पंक्ति को फ़ील्ड (स्तंभ) मानों की सूची के रूप में लौटी सूची में दर्शाया गया है। कॉलम नामों को लौटी हुई सूची की पहली प्रविष्टि के रूप में प्रदान किया जा सकता है, इसलिए कॉलिंग रूटीन में लौटी सूची को पार्स करना वास्तव में आसान और लचीला हो सकता है। इस तरह, डेटाबेस कॉल करने वाली दिनचर्या को उस डेटा के बारे में कुछ भी जानने की आवश्यकता नहीं है जिसे वह संभाल रहा है। यहाँ इस तरह की एक दिनचर्या है:

    def read_DB_Records(self, tablename, fieldlist, wherefield, wherevalue) -> list:

        DBfile = 'C:/DATA/MyDatabase.accdb'
        # this connection string is for Access 2007, 2010 or later .accdb files
        conn = pyodbc.connect(r'Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ='+DBfile)
        cursor = conn.cursor()

        # Build the SQL Query string using the passed-in field list:
        SQL = "SELECT "
        for i in range(0, len(fieldlist)):
            SQL = SQL + "[" + fieldlist[i] + "]"
            if i < (len(fieldlist)-1):
                SQL = SQL + ", "
        SQL = SQL + " FROM " + tablename

        # Support an optional WHERE clause:
        if wherefield != "" and wherevalue != "" :
            SQL = SQL + " WHERE [" + wherefield + "] = " + "'" + wherevalue + "';"

        results = []    # Create the results list object

        cursor.execute(SQL) # Execute the Query

        # (Optional) Get a list of the column names returned from the query:
        columns = [column[0] for column in cursor.description]
        results.append(columns) # append the column names to the return list

        # Now add each row as a list of column data to the results list
        for row in cursor.fetchall():   # iterate over the cursor
            results.append(list(row))   # add the row as a list to the list of lists

        cursor.close()  # close the cursor
        conn.close()    # close the DB connection

        return results  # return the list of lists

1

मुझे @bryan और @ फू-स्टैक उत्तर पसंद हैं। आप PostgreSQL के साथ काम कर रहे हैं और आप उपयोग कर रहे हैं psycopg2आप कुछ इस्तेमाल कर सकते हैं psycopg2 से उपहार cursorfactory किया जा रहा है एक निर्दिष्ट करने के द्वारा एक ही प्राप्त करने के लिए DictCursorजब कनेक्शन से अपने कर्सर बनाने, इस तरह:

cur = conn.cursor( cursor_factory=psycopg2.extras.DictCursor )

तो अब आप अपनी sql क्वेरी को निष्पादित कर सकते हैं और आपको अपने परिणामों को लाने के लिए एक शब्दकोश मिलेगा, बिना हाथ से उन्हें मैप करने की आवश्यकता के बिना।

cur.execute( sql_query )
results = cur.fetchall()

for row in results:
    print row['row_no']

कृपया ध्यान दें कि आपको import psycopg2.extrasउस काम के लिए होना पड़ेगा ।


0

मान लें कि आप कॉलम नाम जानते हैं! इसके अलावा, यहां तीन अलग-अलग समाधान हैं,
आप शायद पिछले एक को देखना चाहते हैं!

colnames = ['city', 'area', 'street']
data = {}

counter = 0
for row in x.fetchall():
    if not counter in data:
        data[counter] = {}

    colcounter = 0
    for colname in colnames:
        data[counter][colname] = row[colcounter]
        colcounter += 1

    counter += 1

यह एक अनुक्रमित संस्करण है, सबसे सुंदर समाधान नहीं है लेकिन यह काम करेगा। पंक्ति संख्या के क्रम में डेटा वाली प्रत्येक कुंजी के भीतर एक सूची के साथ शब्दकोष कुंजी के रूप में कॉलम नाम को अनुक्रमित करना एक और होगा। ऐसा करके:

colnames = ['city', 'area', 'street']
data = {}

for row in x.fetchall():
    colindex = 0
    for col in colnames:
        if not col in data:
            data[col] = []
        data[col].append(row[colindex])
        colindex += 1

इसे लिखते हुए, मैं समझता हूं कि कर की for col in colnamesजगह ले ली जा सकती है for colindex in range(0, len())लेकिन आपको इसका विचार है। उदाहरण के लिए, बाद का उदाहरण उपयोगी होगा जब सभी डेटा को नहीं लाया जाएगा, लेकिन उदाहरण के लिए एक बार में एक पंक्ति:

डेटा की प्रत्येक पंक्ति के लिए तानाशाही का उपयोग करना

def fetchone_dict(stuff):
    colnames = ['city', 'area', 'street']
    data = {}

    for colindex in range(0, colnames):
        data[colnames[colindex]] = stuff[colindex]
    return data

row = x.fetchone()
print fetchone_dict(row)['city']

Tablenames हो रही है (मुझे लगता है .. फू स्टैक के लिए धन्यवाद): नीचे दाढ़ी से
एक अधिक प्रत्यक्ष समाधान !

cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';")
schema = {}
for it in cursor.fetchall():
    if it[0] in schema:
       schema[it[0]].append(it[1])
    else:
        schema[it[0]] = [it[1]]

धन्यवाद, लेकिन जब मैं अपने कॉलम के नाम नहीं जानता, तो क्या इसके लिए एक सामान्यीकृत समाधान है?
फू स्टैक

हाँ, इसे SQL सिंटैक्स कहा जाता है। आप अपने डेटाबेस को उस तालिका के नाम के लिए क्वेरी कर सकते हैं जिसके खिलाफ आप क्वेरी कर रहे हैं। stackoverflow.com/questions/4645456/…
Torxed

मैंने एक अच्छा सा सामान्यीकृत स्कीमा संग्रहक लिखा है:
फू स्टैक

1
cursor.execute("SELECT sys.objects.name, sys.columns.name FROM sys.objects INNER JOIN sys.columns ON sys.objects.object_id = sys.columns. object_id WHERE sys.objects.type = 'U';") schema = {} for it in cursor.fetchall(): if it[0] in schema: schema[it[0]].append(it[1]) else: schema[it[0]] = [it[1]]
फू स्टैक

@FooStack कॉलम नाम पहले से ही कर्सर में दिए गए हैं । एक अलग क्वेरी की जरूरत नहीं है।
ब्रायन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.