Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #100322

Re: Problem with sqlite3 and Decimal

Path csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail
From "Frank Millman" <frank@chagford.com>
Newsgroups comp.lang.python
Subject Re: Problem with sqlite3 and Decimal
Date Sat, 12 Dec 2015 09:31:33 +0200
Lines 82
Message-ID <mailman.173.1449905509.12405.python-list@python.org> (permalink)
References <n4ei3l$b98$1@ger.gmane.org>
Mime-Version 1.0
Content-Type text/plain; format=flowed; charset="iso-8859-1"; reply-type=response
Content-Transfer-Encoding 7bit
X-Trace news.uni-berlin.de hp6NqJd6OwLIh+Hk/Uw3mALj3dB5o0+KAR2D2L3ytJRQ==
Return-Path <python-python-list@m.gmane.org>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.001
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'python,': 0.02; 'string.': 0.04; 'rewrite': 0.07; 'seemed': 0.07; 'subject:sqlite3': 0.07; "'#'": 0.09; 'adapter': 0.09; 'internally': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'str)': 0.09; 'python': 0.10; 'def': 0.13; "skip:' 30": 0.15; 'lambda': 0.16; 'received:80.91.229.3': 0.16; 'received:io': 0.16; 'received:plane.gmane.org': 0.16; 'received:psf.io': 0.16; 'row': 0.16; 'second-guess': 0.16; 'sqlite3': 0.16; 'subject:Problem': 0.16; 'true:': 0.16; 'user-defined': 0.16; 'integer': 0.18; 'passes': 0.18; 'string,': 0.18; 'runs': 0.18; 'windows': 0.20; 'function,': 0.22; 'precise': 0.22; 'pass': 0.22; 'trying': 0.22; 'bit': 0.23; 'wrote': 0.23; 'header:In-Reply-To:1': 0.24; 'module': 0.25; 'header:X-Complaints-To:1': 0.26; 'required.': 0.26; 'addition,': 0.27; 'followed': 0.27; 'back.': 0.27; 'converting': 0.27; 'function': 0.28; 'arithmetic': 0.29; 'decimal': 0.29; 'figured': 0.29; 'forces': 0.29; 'workaround': 0.29; 'objects': 0.29; 'allows': 0.30; 'connection': 0.30; 'normally': 0.30; 'putting': 0.30; 'skip:s 30': 0.31; 'table': 0.32; 'statement': 0.32; 'run': 0.33; 'point': 0.33; 'problem': 0.33; 'skip:d 20': 0.34; 'skip:c 30': 0.35; 'could': 0.35; 'mix': 0.35; 'problem.': 0.35; 'but': 0.36; 'needed': 0.36; 'to:addr :python-list': 0.36; 'subject:: ': 0.37; 'received:org': 0.37; 'wanted': 0.37; 'skip:p 20': 0.38; 'test': 0.39; 'does': 0.39; 'to:addr:python.org': 0.40; 'where': 0.40; 'subject:with': 0.40; 'hope': 0.61; 'back': 0.62; 'more': 0.63; 'within': 0.64; 'balance': 0.64; 'converter': 0.66; 'effective.': 0.66; 'here': 0.66; 'situation': 0.67; 'therefore': 0.67; 'frank': 0.72; 'article': 0.77; 'interest.': 0.79; '3.4': 0.84; 'bal': 0.84; 'clearly.': 0.84; 'together,': 0.84; 'habit': 0.91; 'inefficient': 0.91; 'hand,': 0.97
X-Injected-Via-Gmane http://gmane.org/
X-Gmane-NNTP-Posting-Host 197.82.211.31
In-Reply-To <n4ei3l$b98$1@ger.gmane.org>
X-MSMail-Priority Normal
Importance Normal
X-Newsreader Microsoft Windows Live Mail 15.4.3502.922
X-MimeOLE Produced By Microsoft MimeOLE V15.4.3502.922
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.20+
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <https://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list/>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <https://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
Xref csiph.com comp.lang.python:100322

Show key headers only | View raw


"Frank Millman"  wrote in message news:n4ei3l$b98$1@ger.gmane.org...

> I need to store Decimal objects in a sqlite3 database, using Python 3.4 on 
> Windows 7.
>
> I followed the instructions here -
>
> 
> http://stackoverflow.com/questions/6319409/how-to-convert-python-decimal-to-sqlite-numeric
>
> It seemed to work well, but then I hit a problem.
>
[...]

I have found a workaround for my problem, but first I needed to understand 
what was going on more clearly. This is what I have figured out.

1. The solution in the SO article is a bit of sleight of hand, though very 
effective. It does not create a Decimal type in sqlite3. It simply provides 
a way of converting Decimal objects to strings when you pass them into the 
database, and converting them back to Decimal types when you read them back.

2. This works if you only use sqlite3 as a storage mechanism, and use Python 
to perform any arithmetic required. It fails when you try to use sqlite3 to 
perform arithmetic, as it uses floating point internally and suffers from 
the same problem that Python does when trying to mix floating point and 
precise decimal representation.

3. Normally I do use Python to perform the arithmetic, but in this situation 
I wanted to do the following -

    UPDATE table SET balance = balance + ? WHERE date > ?

It would be very inefficient to read every row into Python, perform the 
addition, and write it back again.

4. The Python sqlite3 module allows you to create a user-defined function 
that you can use from within SQL statements. I realised I could use this to 
get the best of both worlds. I wrote the following function -

    def aggregate(curr_value, aggr_value):
        return '#{}'.format(D(curr_value[1:]) + D(aggr_value[1:]))

and added this to the connection -

    conn.create_function('aggregate', 2, aggregate)

I could then rewrite my statement as -

    UPDATE table SET balance = aggregate(balance, ?) WHERE date > ?

5. The reason for the '#' in the above function is that sqlite3 passes the 
current value of 'balance' into my function, and it has a bad habit of 
trying to second-guess the data-type to use. Even though I store it as a 
string, it passes in an integer or float. Prefixing it with a '#' forces it 
to remain as a string.

My adapters therefore now look like this -

    # Decimal adapter (store Decimal in database as str)
    sqlite3.register_adapter(D, lambda d:'#'+str(d))

    # Decimal converter (convert back to Decimal on return)
    sqlite3.register_converter('DEC', lambda s: D(s.decode('utf-8')[1:]))

6. Putting it all together, I can now run my test program -

    while True:
        print(cur.execute("SELECT bal FROM fmtemp").fetchone()[0])
        cur.execute("UPDATE fmtemp SET bal = aggregate(bal, ?)", 
(D('123.45'),))
        q = input()
        if q == 'q':
            break

and it runs up to 123450.00 without misbehaving.

Hope this is of interest.

Frank

Back to comp.lang.python | Previous | Next | Find similar | Unroll thread


Thread

Re: Problem with sqlite3 and Decimal "Frank Millman" <frank@chagford.com> - 2015-12-12 09:31 +0200

csiph-web