19

I've been using the following function to make a "more readable" (supposedly) format for fetching data from Oracle. Here is the function:

def rows_to_dict_list(cursor):
    """ 
    Create a list, each item contains a dictionary outlined like so:
    { "col1_name" : col1_data }
    Each item in the list is technically one row of data with named columns,
    represented as a dictionary object
    For example:
    list = [
        {"col1":1234567, "col2":1234, "col3":123456, "col4":BLAH},
        {"col1":7654321, "col2":1234, "col3":123456, "col4":BLAH}
    ]
    """

    # Get all the column names of the query.
    # Each column name corresponds to the row index
    # 
    # cursor.description returns a list of tuples, 
    # with the 0th item in the tuple being the actual column name.
    # everything after i[0] is just misc Oracle info (e.g. datatype, size)
    columns = [i[0] for i in cursor.description]

    new_list = []
    for row in cursor:
        row_dict = dict()
        for col in columns:
            # Create a new dictionary with field names as the key, 
            # row data as the value.
            #
            # Then add this dictionary to the new_list
            row_dict[col] = row[columns.index(col)]

        new_list.append(row_dict)
    return new_list

I would then use the function like this:

sql = "Some kind of SQL statement"
curs.execute(sql)
data = rows_to_dict_list(curs)
#
for row in data:
    item1 = row["col1"]
    item2 = row["col2"]
    # Do stuff with item1, item2, etc...
    # You don't necessarily have to assign them to variables,
    # but you get the idea.

While this seems to perform fairly well under varying levels of stress, I'm wondering if there's a more efficient, or "pythonic" way of doing this.

6 Answers 6

29

There are other improvements to make, but this really jumped out at me:

    for col in columns:
        # Create a new dictionary with field names as the key, 
        # row data as the value.
        #
        # Then add this dictionary to the new_list
        row_dict[col] = row[columns.index(col)]

In addition to being inefficient, using index in situations like this is bug-prone, at least in situations where the same item may occur twice in a list. Use enumerate instead:

    for i, col in enumerate(columns):
        # Create a new dictionary with field names as the key, 
        # row data as the value.
        #
        # Then add this dictionary to the new_list
        row_dict[col] = row[i]

But that's small potatoes, really. Here's a much more compact version of this function:

def rows_to_dict_list(cursor):
    columns = [i[0] for i in cursor.description]
    return [dict(zip(columns, row)) for row in cursor]

Let me know if that works.

Sign up to request clarification or add additional context in comments.

2 Comments

What if one needs to do some post processing on elements of each row. Then this wouldn`t work -> [dict(zip(columns, row)) for row in cursor]
@ramu, that sounds like a new question to me. If someone hasn't already asked it here, perhaps you should.
10

For a clean way to avoid the memory usage of dumping everything in a list upfront, you could wrap the cursor in a generator function:

def rows_as_dicts(cursor):
    """ returns cx_Oracle rows as dicts """
    colnames = [i[0] for i in cursor.description]
    for row in cursor:
        yield dict(zip(colnames, row))

Then use as follows - rows from the cursor are converted to dicts while iterating:

for row in rows_as_dicts(cursor):
    item1 = row["col1"]
    item2 = row["col2"]

4 Comments

This is probably good for large result sets, but I found it to be less performant than @senderle's answer for relatively small result sets.
@bspkrs Thanks - i could see that. Do you have any numbers on actual performance difference you saw?
Python also allows you to inline generators: for row in (i[0] for i in cursor.description):. No need for a separate function.
@jpmc26 That's a great suggestion - I tend to forget about that!
4

You shouldn't use dict for big result sets because the memory usage will be huge. I use cx_Oracle a lot and not have a nice dictionary cursor bothered me enough to write a module for it. I also have to connect Python to many different databases so I did it in a way that you can use with any DB API 2 connector.

It's up on PyPi DBMS - DataBases Made Simpler

>>> import dbms
>>> db = dbms.OraConnect('myUser', 'myPass', 'myInstance')
>>> cur = db.cursor()
>>> cur.execute('SELECT * FROM people WHERE id = :id', {'id': 1123})
>>> row = cur.fetchone()
>>> row['last_name']
Bailey
>>> row.last_name
Bailey
>>> row[3]
Bailey
>>> row[0:4]
[1123, 'Scott', 'R', 'Bailey']

Comments

0

Assume cursor "Cursor" is already defined and raring to go:

byCol = {cl:i for i,(cl,type, a, b, c,d,e) in enumerate(Cursor.description)}

then you can just go:

for row in Cursor: column_of_interest = row[byCol["COLUMN_NAME_OF_INTEREST"]]

Not as clean and smooth as if the system handled it itself, but not horrible.

Comments

0

Create a dict

cols=dict()
for col, desc in enumerate(cur.description):
    cols[desc[0]] = col

To access:

for result in cur
    print (result[cols['COL_NAME']])

Comments

-1

I have a better one:

import cx_Oracle

def makedict(cursor):
"""Convert cx_oracle query result to be a dictionary   
"""
cols = [d[0] for d in cursor.description]

def createrow(*args):
    return dict(zip(cols, args))

return createrow

db = cx_Oracle.connect('user', 'pw', 'host')
cursor = db.cursor()
rs = cursor.execute('SELECT * FROM Tablename')
cursor.rowfactory = makedict(cursor)

1 Comment

This answer really makes no sense. Or at least the example you put in here. you define 2 functions which you don't even call in your example and your indents are also not correct. edit: nvm you call them but your indents are so out of line its really confusing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.