Help cleaning up some code

O

odeits

I am looking to clean up this code... any help is much appreciated.
Note: It works just fine, I just think it could be done cleaner.

The result is a stack of dictionaries. the query returns up to
STACK_SIZE ads for a user. The check which i think is very ugly is
putting another contraint saying that all of the ni have to be the
same.

stack = []
rows = self.con.execute(adquerystring,(user,STACK_SIZE)).fetchall()
for row in rows:
ad = dict()
ad['ni'] = row['ni']
ad['adid'] = row['adid']
ad['rundateid'] = row['rundateid']
ad['rundate'] = row['rundate']
if row['city'] is None:
ad['city'] = 'None'
else:
ad['city'] = row['city']
if row['state'] is None:
ad['state'] = 'None'
else:
ad['state'] = row['state']
ad['status'] = row['status']
try:
if stack[0]['ni'] != ad['ni']:
break;
except IndexError:
pass
stack.append(ad)
 
P

Paul Rubin

andrew cooke said:
def copy(src, dst, name, quote_none=False):
value = src[name]
dst[name] = 'None' if quote_none and value is None else value

def copy(src, dst, name, default=None):
value = src[name]
dst[name] = value if value is not None else default
copy(row, ad, name, quote_none=True)

copy(row, ad, name, default='None')
 
G

Gerard Flanagan

odeits said:
I am looking to clean up this code... any help is much appreciated.
Note: It works just fine, I just think it could be done cleaner.

The result is a stack of dictionaries. the query returns up to
STACK_SIZE ads for a user. The check which i think is very ugly is
putting another contraint saying that all of the ni have to be the
same.

stack = []
rows = self.con.execute(adquerystring,(user,STACK_SIZE)).fetchall()
for row in rows:
ad = dict()
ad['ni'] = row['ni']
ad['adid'] = row['adid']
ad['rundateid'] = row['rundateid']
ad['rundate'] = row['rundate']
if row['city'] is None:
ad['city'] = 'None'
else:
ad['city'] = row['city']
if row['state'] is None:
ad['state'] = 'None'
else:
ad['state'] = row['state']
ad['status'] = row['status']
try:
if stack[0]['ni'] != ad['ni']:
break;
except IndexError:
pass
stack.append(ad)

Random ideas:

def fetchsomedata(self, user):
query = 'some query'
return list(self._fetch(query, user, STACKSIZE))

def _fetchsomedata(self, query, *params):
keys = 'adid rundateid rundate status'.split()
specialkeys = 'city state'.split()
nikey = 'ni'
ni = None
for row in self.con.execute(query, params).fetchall():
nival = row[nikey]
if ni != nival:
break
if ni is None:
ni = nival
data = {nikey: nival}
for k in keys:
data[k] = row[k]
for k in specialkeys:
data[k] = 'None' if row[k] is None else row[k]
yield data
 
M

Martin P. Hellwig

odeits said:
I am looking to clean up this code... any help is much appreciated.
Note: It works just fine, I just think it could be done cleaner.

The result is a stack of dictionaries. the query returns up to
STACK_SIZE ads for a user. The check which i think is very ugly is
putting another contraint saying that all of the ni have to be the
same.

stack = []
rows = self.con.execute(adquerystring,(user,STACK_SIZE)).fetchall()
for row in rows:
ad = dict()
ad['ni'] = row['ni']
ad['adid'] = row['adid']
ad['rundateid'] = row['rundateid']
ad['rundate'] = row['rundate']
if row['city'] is None:
ad['city'] = 'None'
else:
ad['city'] = row['city']
if row['state'] is None:
ad['state'] = 'None'
else:
ad['state'] = row['state']
ad['status'] = row['status']
try:
if stack[0]['ni'] != ad['ni']:
break;
except IndexError:
pass
stack.append(ad)
odeits said:
> I am looking to clean up this code... any help is much appreciated.
> Note: It works just fine, I just think it could be done cleaner.
>
> The result is a stack of dictionaries. the query returns up to
> STACK_SIZE ads for a user. The check which i think is very ugly is
> putting another contraint saying that all of the ni have to be the
> same.
>
> stack = []
> rows = self.con.execute(adquerystring,(user,STACK_SIZE)).fetchall()
> for row in rows:
> ad = dict()
> ad['ni'] = row['ni']
> ad['adid'] = row['adid']
> ad['rundateid'] = row['rundateid']
> ad['rundate'] = row['rundate']
> if row['city'] is None:
> ad['city'] = 'None'
> else:
> ad['city'] = row['city']
> if row['state'] is None:
> ad['state'] = 'None'
> else:
> ad['state'] = row['state']
> ad['status'] = row['status']
> try:
> if stack[0]['ni'] != ad['ni']:
> break;
> except IndexError:
> pass
> stack.append(ad)

I would write it (untested) something like this:

def query_parser(QUERY, USER, STACK_SIZE):
indexes = ['ni','adid','rundateid','rundate','city','state','status']
empty = 'None'

stack = []
query_result = self.con.execute(QUERY,(USER,STACK_SIZE)).fetchall()
ni = indexes[0]

for row in query_result:
temp_dict = dict()

if len(stack) > 0:
if stack[0][ni] != row[ni]:
# Break if the current ni is not equal to the first
break

else:
for item in indexes:
try:
temp_dict[item] = row[item]
except:
temp_dict[item] = empty

stack.append(temp_dict)

return(stack)

hth
 
M

Martin P. Hellwig

Martin P. Hellwig wrote:
def query_parser(QUERY, USER, STACK_SIZE):
indexes = ['ni','adid','rundateid','rundate','city','state','status']
empty = 'None'

stack = []
query_result = self.con.execute(QUERY,(USER,STACK_SIZE)).fetchall()
ni = indexes[0]

for row in query_result:
temp_dict = dict()

if len(stack) > 0:
if stack[0][ni] != row[ni]:
# Break if the current ni is not equal to the first
break

else:
for item in indexes:
try:
temp_dict[item] = row[item]
except:
temp_dict[item] = empty

stack.append(temp_dict)

return(stack)

hth

Hold my horses :), I made logical errors in the break, else and for
item parts (thats what you get from rewriting instead of rethinking ;-) )

This should be more like it:

def query_parser(QUERY, USER, STACK_SIZE):
indexes = ['ni','adid','rundateid','rundate','city','state','status']
empty = 'None'

stack = []
query_result = self.con.execute(QUERY,(USER,STACK_SIZE)).fetchall()
ni = indexes[0]

for row in query_result:
temp_dict = dict()
ignore = False

if len(stack) > 0:
if stack[0][ni] != row[ni]:
ignore = True

if ignore == False:
for item in indexes:
try:
temp_dict[item] = row[item]
except:
temp_dict[item] = empty

stack.append(temp_dict)

return(stack)

Though it is likely I made another stupid mistake.
 
M

Martin P. Hellwig

Martin P. Hellwig wrote:
while I am at it :)

if ignore == False:

is probably cleaner when written

if not ignore:
 
D

Dennis Lee Bieber

Thanks a lot! that copy function is a really neat trick!

Yes the ni's are sorted, and yes the query SHOULD be the one to limit it to
just one set of ni's; however, i am having trouble implementing that in sql.
I am using sqlite3 atm and right now i have a very large select statment the
gets me what i want, i just dont know how to filter it again base on data
that is from the result itself. I THINK normally one would select into a
temporary table then filter it from there, but i am not sure.

Not having seen the SQL I can't really comment... But if the common
WHERE, LIMIT, and OFFSET clauses aren't sufficient, you might try a
subselect:

select final, stuff from
(select intermediate, stuff from .... where ...)
where ...
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
O

odeits

odeits said:
I am looking to clean up this code... any help is much appreciated.
Note: It works just fine, I just think it could be done cleaner.
The result is a stack of dictionaries. the query returns up to
STACK_SIZE ads for a user. The check which i think is very ugly is
putting another contraint saying that all of the ni have to be the
same.

Well, the obvious way to get your constraint is by changing your SQL,
but if you are going to do it by fetching rows, try:

     FIELDS = 'ni adid rundateid rundate city state status'.split()
     ni = UNSET = object() # use None unless None might be the value
     stack = []
     rows = self.con.execute(adquerystring, (user,STACK_SIZE)).fetchall()
     for row in rows:
         ad = dict()
         for field in FIELDS:
             ad[field] = row[field]
         for field in 'city', 'state':
             if ad[field] is None:
                 ad[field] = 'None'
         if ni != ad['ni']:
             if ni is UNSET:
                 ni = ad['ni']
             else:
                 break
         stack.append(ad)

--Scott David Daniels
(e-mail address removed)

Taking from several suggestions this is what i have come up with for
now:

for row in ifilter(lambda r: r['ni'] == rows[0]['ni'],rows):
ad = dict()

keys = row.keys() # if python 2.6
keys =
['ni','adid','rundateid','rundate','city','state','status'] # if
python 2.5

for index in row.keys():
if row[index] is None:
ad[index] = 'None'
else:
ad[index] = row[index]
stack.append(ad)
print row

the test to see if the ad is valid is placed in the ifilter so that I
dont build the dictionary unnecessarily. and the None special case is
fairly simple to read now. The None case would even be irrelevant if i
could get the damn xmlrpc to allow null. sigh. anyhow. thanks for all
of your input, it is definitely better than it was ;)
 
O

odeits

Well, the obvious way to get your constraint is by changing your SQL,
but if you are going to do it by fetching rows, try:
     FIELDS = 'ni adid rundateid rundate city state status'.split()
     ni = UNSET = object() # use None unless None might be the value
     stack = []
     rows = self.con.execute(adquerystring, (user,STACK_SIZE)).fetchall()
     for row in rows:
         ad = dict()
         for field in FIELDS:
             ad[field] = row[field]
         for field in 'city', 'state':
             if ad[field] is None:
                 ad[field] = 'None'
         if ni != ad['ni']:
             if ni is UNSET:
                 ni = ad['ni']
             else:
                 break
         stack.append(ad)
--Scott David Daniels
(e-mail address removed)

Taking from several suggestions this is what i have come up with for
now:

         for row in  ifilter(lambda r: r['ni'] == rows[0]['ni'],rows):
            ad = dict()

            keys = row.keys() # if python 2.6
            keys =
['ni','adid','rundateid','rundate','city','state','status'] # if
python 2.5

            for index in row.keys():
                if row[index] is None:
                    ad[index] = 'None'
                else:
                    ad[index] = row[index]
            stack.append(ad)
            print row

the test to see if the ad is valid is placed in the ifilter so that I
dont build the dictionary unnecessarily. and the None special case is
fairly simple to read now. The None case would even be irrelevant if i
could get the damn xmlrpc to allow null. sigh. anyhow. thanks for all
of your input, it is definitely better than it was ;)

For those of you who asked about the SQL the full function is here.
The connection is to a sqlite database with the row_factory set to
sqlite.Row


def get(self,user):
'''
Fetches an ad for USER, assigns the ad and returns a
dictionary that represents an ad
'''

stack = []
aduserquerystring = "SELECT ni, adid, rundateid, rundate,
city, state, status, priority,time FROM ads NATURAL JOIN rundates
NATURAL JOIN newspapers WHERE ( status in (1,3) and user = ?) "
expiredadquerystring = "SELECT ni, adid, rundateid, rundate,
city, state, status, priority,time FROM ads NATURAL JOIN rundates
NATURAL JOIN newspapers WHERE ( status = 1 AND time < DATETIME
('NOW', '-%d MINUTES') ) LIMIT 0,?"%(MINUTES_TO_EXPIRE,)
adquerystring = "SELECT ni, adid, rundateid, rundate, city,
state, status , priority, time FROM ads NATURAL JOIN rundates NATURAL
JOIN newspapers WHERE (status IN (0,2) AND priority IN ( SELECT
priority FROM users NATURAL JOIN groups WHERE user = ? )) ORDER BY
status DESC, priority ASC, ni ASC, time ASC, adid ASC LIMIT 0,?"

rows = self.con.execute(aduserquerystring,(user,)).fetchall()
if len(rows) == 0:
rows = self.con.execute(expiredadquerystring,
(STACK_SIZE,)).fetchall()
if len(rows) == 0:
rows = self.con.execute(adquerystring,
(user,STACK_SIZE)).fetchall()
print user
keys =
['ni','adid','rundateid','rundate','status','city','state']

for row in ifilter(lambda r: r['ni'] == rows[0]['ni'], rows):
ad = dict( )

for key in keys:
if row[key] is None:
ad[key] = 'None'
else:
ad[key] = row[key]


stack.append(ad)
print row

self.con.executemany('UPDATE ads SET user = ?, status = CASE
(status) WHEN 1 THEN 1 WHEN 0 THEN 1 WHEN 2 THEN 3 END WHERE adid = ?',
[(user, ad['adid']) for ad in stack])
self.con.commit()


return stack
 
O

odeits

odeits said:
odeits wrote:
I am looking to clean up this code... any help is much appreciated.
Note: It works just fine, I just think it could be done cleaner.
The result is a stack of dictionaries. the query returns up to
STACK_SIZE ads for a user. The check which i think is very ugly is
putting another contraint saying that all of the ni have to be the
same.
Well, the obvious way to get your constraint is by changing your SQL,
but if you are going to do it by fetching rows, try:
     FIELDS = 'ni adid rundateid rundate city state status'.split()
     ni = UNSET = object() # use None unless None might be the value
     stack = []
     rows = self.con.execute(adquerystring,
(user,STACK_SIZE)).fetchall()
     for row in rows:
         ad = dict()
         for field in FIELDS:
             ad[field] = row[field]
         for field in 'city', 'state':
             if ad[field] is None:
                 ad[field] = 'None'
         if ni != ad['ni']:
             if ni is UNSET:
                 ni = ad['ni']
             else:
                 break
         stack.append(ad)
--Scott David Daniels
(e-mail address removed)
Taking from several suggestions this is what i have come up with for
now:
         for row in  ifilter(lambda r: r['ni'] == rows[0]['ni'],rows):

not sure what version of python you're using, but it would be more natural
in recent python to write that as:

         for row in (r for r in rows if r['ni'] == rows[0]['ni']):

(the () create a generator for you).

andrew
            ad = dict()
            keys = row.keys() # if python 2.6
            keys =
['ni','adid','rundateid','rundate','city','state','status'] # if
python 2.5
            for index in row.keys():
                if row[index] is None:
                    ad[index] = 'None'
                else:
                    ad[index] = row[index]
            stack.append(ad)
            print row
the test to see if the ad is valid is placed in the ifilter so that I
dont build the dictionary unnecessarily. and the None special case is
fairly simple to read now. The None case would even be irrelevant if i
could get the damn xmlrpc to allow null. sigh. anyhow. thanks for all
of your input, it is definitely better than it was ;)

This function is very time critical so i went with itertools "for
efficient looping" and i am runing 2.5
 
D

Dennis Lee Bieber

For those of you who asked about the SQL the full function is here.
The connection is to a sqlite database with the row_factory set to
sqlite.Row


def get(self,user):
'''
Fetches an ad for USER, assigns the ad and returns a
dictionary that represents an ad
'''

stack = []
aduserquerystring = "SELECT ni, adid, rundateid, rundate,
city, state, status, priority,time FROM ads NATURAL JOIN rundates
NATURAL JOIN newspapers WHERE ( status in (1,3) and user = ?) "
expiredadquerystring = "SELECT ni, adid, rundateid, rundate,
city, state, status, priority,time FROM ads NATURAL JOIN rundates
NATURAL JOIN newspapers WHERE ( status = 1 AND time < DATETIME
('NOW', '-%d MINUTES') ) LIMIT 0,?"%(MINUTES_TO_EXPIRE,)
adquerystring = "SELECT ni, adid, rundateid, rundate, city,
state, status , priority, time FROM ads NATURAL JOIN rundates NATURAL
JOIN newspapers WHERE (status IN (0,2) AND priority IN ( SELECT
priority FROM users NATURAL JOIN groups WHERE user = ? )) ORDER BY
status DESC, priority ASC, ni ASC, time ASC, adid ASC LIMIT 0,?"

Not knowing which fields come from which tables (nor which are
commonly named per the definition of natural join) the first thing that
strikes me here is that it looks like you could create a view/virtual
table in the database to consolidate that

... rundates natural join newspapers ...

And do your tables really share that many columns? Since the
definition of the natural join is basically an inner/equi-join over
every column that has the same name in the two tables, you are forcing
the SQL engine to basically loop over all the columns in first table,
looking for matching column name in the second table, and computing a
"t1.colname = t2.colname" for EACH matching column.

If there are many such columns in common, I'd be looking to factor
them out into a separate table with an integer primary key, and put the
foreign keys into the original tables.
rows = self.con.execute(aduserquerystring,(user,)).fetchall()
if len(rows) == 0:

I suspect a simple

if not rows:

can be used.
rows = self.con.execute(expiredadquerystring,
(STACK_SIZE,)).fetchall()
if len(rows) == 0:
rows = self.con.execute(adquerystring,
(user,STACK_SIZE)).fetchall()

In the worst case you are using three separate hits on the database
(and recomputing the same natural joins in many cases each time --
definitely suggest creating a view for the basic joins to simplify the
parsing -- if not a physical read-only table, though that would need to
be refreshed at times).

A one-time database hit:

SQL = """select 1, ni, adid, rundateid, rundate, city,
state, status, priority, time
from ads natural join rundates
natural join newspapers
where status in (1, 3) and user = :USER

UNION
select 2, ni, adid, rundateid, rundate, city,
state, status, priority, time
from ads natural join rundates
natural join newspapers
where status = 1 and time < datetime("now", "-%d minutes")
limit 0, :STACK

UNION
select 3, ni, adid, rundateid, rundate, city,
state, status, priority, time
from ads natural join rundates
natural join newspapers
where status in (0, 2) and priority in
(select priority from users natural join groups
where user = :USER)
order by status desc, priority, ni, time, adid
limit 0, :STACK""" % (MINUTES_TO_EXPIRE,)

rows = self.con.execute(SQL,
{"USER" : user, "STACK" : STACK_SIZE}).fetchall()

I've added a column which identifies which of the three select
groups the data came from.

#filter out unwanted data
#(if select 1 returned data, exclude select 2 and 3)
if rows:
grp = rows[0][0]
rows = [r for r in rows if r[0] = grp]
print user
keys =
['ni','adid','rundateid','rundate','status','city','state']

for row in ifilter(lambda r: r['ni'] == rows[0]['ni'], rows):
ad = dict( )

for key in keys:
if row[key] is None:
ad[key] = 'None'
else:
ad[key] = row[key]


stack.append(ad)
print row

self.con.executemany('UPDATE ads SET user = ?, status = CASE
(status) WHEN 1 THEN 1 WHEN 0 THEN 1 WHEN 2 THEN 3 END WHERE adid = ?',
[(user, ad['adid']) for ad in stack])

For some reason that case statement just seems to smell to me... I
suspect, since you have retrieved "status" earlier, I'd have used a
dictionary look-up...

update ads set
user = ?,
status = ?
where adid = ?

and

[(user,
{1 : 1, 0 : 1, 2 : 3}[ad["status"]],
ad["adid"]) for ad in stack]

Question: I don't see anything taking the case when status is already 3;
nor does anything (at this level) ever update the value to a 2...
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
O

odeits

For those of you who asked about the SQL the full function is here.
The connection is to a sqlite database with the row_factory set to
sqlite.Row
 def get(self,user):
        '''
            Fetches an ad for USER, assigns the ad and returns a
dictionary that represents an ad
        '''
        stack = []
        aduserquerystring = "SELECT ni, adid, rundateid, rundate,
city, state, status, priority,time FROM ads NATURAL JOIN rundates
NATURAL JOIN newspapers WHERE  ( status in (1,3) and user = ?) "
        expiredadquerystring  = "SELECT ni, adid, rundateid, rundate,
city, state, status, priority,time FROM ads NATURAL JOIN rundates
NATURAL JOIN newspapers WHERE   ( status = 1 AND time < DATETIME
('NOW', '-%d MINUTES') ) LIMIT 0,?"%(MINUTES_TO_EXPIRE,)
        adquerystring = "SELECT ni, adid, rundateid, rundate, city,
state, status , priority, time FROM ads NATURAL JOIN rundates NATURAL
JOIN newspapers WHERE (status IN (0,2) AND priority IN ( SELECT
priority FROM users NATURAL JOIN groups WHERE user = ? ))  ORDER BY
status DESC, priority ASC, ni ASC,  time ASC, adid ASC LIMIT 0,?"

        Not knowing which fields come from which tables (nor which are
commonly named per the definition of natural join) the first thing that
strikes me here is that it looks like you could create a view/virtual
table in the database to consolidate that

        ... rundates natural join newspapers ...

        And do your tables really share that many columns? Since the
definition of the natural join is basically an inner/equi-join over
every column that has the same name in the two tables, you are forcing
the SQL engine to basically loop over all the columns in first table,
looking for matching column name in the second table, and computing a
"t1.colname = t2.colname" for EACH matching column.

        If there are many such columns in common, I'd be looking to factor
them out into a separate table with an integer primary key, and put the
foreign keys into the original tables.
        rows = self.con.execute(aduserquerystring,(user,)).fetchall()
        if len(rows) ==  0:

        I suspect a simple

                if not rows:

can be used.
            rows = self.con.execute(expiredadquerystring,
(STACK_SIZE,)).fetchall()
        if len(rows) ==  0:
            rows = self.con.execute(adquerystring,
(user,STACK_SIZE)).fetchall()

        In the worst case you are using three separate hits on the database
(and recomputing the same natural joins in many cases each time --
definitely suggest creating a view for the basic joins to simplify the
parsing -- if not a physical read-only table, though that would need to
be refreshed at times).

        A one-time database hit:

        SQL = """select 1, ni, adid, rundateid, rundate, city,
                                        state, status, priority, time
                        from ads natural join rundates
                        natural join newspapers
                        where status in (1, 3) and user = :USER

                        UNION
                        select 2, ni, adid, rundateid, rundate, city,
                                        state, status, priority, time
                        from ads natural join rundates
                        natural join newspapers
                        where status = 1 and time < datetime("now", "-%d minutes")
                        limit 0, :STACK

                        UNION
                        select 3, ni, adid, rundateid, rundate, city,
                                        state, status, priority, time
                        from ads natural join rundates
                        natural join newspapers
                        where status in (0, 2) and priority in
                                (select priority from users natural join groups
                                        where user = :USER)
                        order by status desc, priority, ni, time, adid
                        limit 0, :STACK"""       % (MINUTES_TO_EXPIRE,)

                rows = self.con.execute(SQL,
                                {"USER" : user, "STACK" : STACK_SIZE}).fetchall()

        I've added a column which identifies which of the three select
groups the data came from.

                #filter out unwanted data
                #(if select 1 returned data, exclude select 2 and 3)
                if rows:
                        grp = rows[0][0]
                        rows = [r for r in rows if r[0] = grp]


        print user
        keys =
['ni','adid','rundateid','rundate','status','city','state']
        for row in  ifilter(lambda r: r['ni'] == rows[0]['ni'], rows):
            ad = dict( )
            for key in keys:
                if row[key] is None:
                    ad[key] = 'None'
                else:
                    ad[key] = row[key]
            stack.append(ad)
            print row
        self.con.executemany('UPDATE ads SET user = ?, status = CASE
(status) WHEN 1 THEN 1 WHEN 0 THEN 1 WHEN 2 THEN 3 END WHERE adid = ?',
[(user, ad['adid']) for ad in stack])

        For some reason that case statement just seems to smell to me... I
suspect, since you have retrieved "status" earlier, I'd have used a
dictionary look-up...

        update ads set
                user = ?,
                status = ?
        where adid = ?

and

        [(user,
                {1 : 1, 0 : 1, 2 : 3}[ad["status"]],
                ad["adid"]) for ad in stack]

Question: I don't see anything taking the case when status is already 3;
nor does anything (at this level) ever update the value to a 2...
--
        Wulfraed        Dennis Lee Bieber               KD6MOG
        (e-mail address removed)             (e-mail address removed)
                HTTP://wlfraed.home.netcom.com/
        (Bestiaria Support Staff:               (e-mail address removed))
                HTTP://www.bestiaria.com/

i get this error when running that query:

sqlite3.OperationalError: LIMIT clause should come after UNION not
before
 
O

odeits

For those of you who asked about the SQL the full function is here.
The connection is to a sqlite database with the row_factory set to
sqlite.Row
 def get(self,user):
        '''
            Fetches an ad for USER, assigns the ad and returns a
dictionary that represents an ad
        '''
        stack = []
        aduserquerystring = "SELECT ni, adid, rundateid, rundate,
city, state, status, priority,time FROM ads NATURAL JOIN rundates
NATURAL JOIN newspapers WHERE  ( status in (1,3) and user = ?) "
        expiredadquerystring  = "SELECT ni, adid, rundateid, rundate,
city, state, status, priority,time FROM ads NATURAL JOIN rundates
NATURAL JOIN newspapers WHERE   ( status = 1 AND time < DATETIME
('NOW', '-%d MINUTES') ) LIMIT 0,?"%(MINUTES_TO_EXPIRE,)
        adquerystring = "SELECT ni, adid, rundateid, rundate, city,
state, status , priority, time FROM ads NATURAL JOIN rundates NATURAL
JOIN newspapers WHERE (status IN (0,2) AND priority IN ( SELECT
priority FROM users NATURAL JOIN groups WHERE user = ? ))  ORDER BY
status DESC, priority ASC, ni ASC,  time ASC, adid ASC LIMIT 0,?"

        Not knowing which fields come from which tables (nor which are
commonly named per the definition of natural join) the first thing that
strikes me here is that it looks like you could create a view/virtual
table in the database to consolidate that

        ... rundates natural join newspapers ...

        And do your tables really share that many columns? Since the
definition of the natural join is basically an inner/equi-join over
every column that has the same name in the two tables, you are forcing
the SQL engine to basically loop over all the columns in first table,
looking for matching column name in the second table, and computing a
"t1.colname = t2.colname" for EACH matching column.

        If there are many such columns in common, I'd be looking to factor
them out into a separate table with an integer primary key, and put the
foreign keys into the original tables.
        rows = self.con.execute(aduserquerystring,(user,)).fetchall()
        if len(rows) ==  0:

        I suspect a simple

                if not rows:

can be used.
            rows = self.con.execute(expiredadquerystring,
(STACK_SIZE,)).fetchall()
        if len(rows) ==  0:
            rows = self.con.execute(adquerystring,
(user,STACK_SIZE)).fetchall()

        In the worst case you are using three separate hits on the database
(and recomputing the same natural joins in many cases each time --
definitely suggest creating a view for the basic joins to simplify the
parsing -- if not a physical read-only table, though that would need to
be refreshed at times).

        A one-time database hit:

        SQL = """select 1, ni, adid, rundateid, rundate, city,
                                        state, status, priority, time
                        from ads natural join rundates
                        natural join newspapers
                        where status in (1, 3) and user = :USER

                        UNION
                        select 2, ni, adid, rundateid, rundate, city,
                                        state, status, priority, time
                        from ads natural join rundates
                        natural join newspapers
                        where status = 1 and time < datetime("now", "-%d minutes")
                        limit 0, :STACK

                        UNION
                        select 3, ni, adid, rundateid, rundate, city,
                                        state, status, priority, time
                        from ads natural join rundates
                        natural join newspapers
                        where status in (0, 2) and priority in
                                (select priority from users natural join groups
                                        where user = :USER)
                        order by status desc, priority, ni, time, adid
                        limit 0, :STACK"""       % (MINUTES_TO_EXPIRE,)

                rows = self.con.execute(SQL,
                                {"USER" : user, "STACK" : STACK_SIZE}).fetchall()

        I've added a column which identifies which of the three select
groups the data came from.

                #filter out unwanted data
                #(if select 1 returned data, exclude select 2 and 3)
                if rows:
                        grp = rows[0][0]
                        rows = [r for r in rows if r[0] = grp]


        print user
        keys =
['ni','adid','rundateid','rundate','status','city','state']
        for row in  ifilter(lambda r: r['ni'] == rows[0]['ni'], rows):
            ad = dict( )
            for key in keys:
                if row[key] is None:
                    ad[key] = 'None'
                else:
                    ad[key] = row[key]
            stack.append(ad)
            print row
        self.con.executemany('UPDATE ads SET user = ?, status = CASE
(status) WHEN 1 THEN 1 WHEN 0 THEN 1 WHEN 2 THEN 3 END WHERE adid = ?',
[(user, ad['adid']) for ad in stack])

        For some reason that case statement just seems to smell to me... I
suspect, since you have retrieved "status" earlier, I'd have used a
dictionary look-up...

        update ads set
                user = ?,
                status = ?
        where adid = ?

and

        [(user,
                {1 : 1, 0 : 1, 2 : 3}[ad["status"]],
                ad["adid"]) for ad in stack]

Question: I don't see anything taking the case when status is already 3;
nor does anything (at this level) ever update the value to a 2...
--
        Wulfraed        Dennis Lee Bieber               KD6MOG
        (e-mail address removed)             (e-mail address removed)
                HTTP://wlfraed.home.netcom.com/
        (Bestiaria Support Staff:               (e-mail address removed))
                HTTP://www.bestiaria.com/

Amazingly enough I tested your compound select statment and found that
it is ~100 times slower than what I currently have. I am trying to
figure out why.
 
D

Dennis Lee Bieber

Amazingly enough I tested your compound select statment and found that
it is ~100 times slower than what I currently have. I am trying to
figure out why.

Well... I do have all three versions in play, whereas your
individual ones only make the three hits if the first two fail, and the
joins are being computed each time.

How many fields are really being used to determine those natural
joins -- if only one field is used per table pair, explicit

inner join .... ON t1.col = t2.col

might offer a speed-up (for both versions).
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
D

Dennis Lee Bieber

i get this error when running that query:

sqlite3.OperationalError: LIMIT clause should come after UNION not
before

Well, I did generate that as a bit of off-the-cuff...

Apparently SQL parses the UNION as a higher precedence than LIMIT --
wanting it to apply to the final results of everything.

And since I'm trying to reduce the number of selects overall, I sure
don't want to suggest making them subselects...

select first long mess
....
union
select * from
(select second long mess
...
limit 0, ?)
union
select * from
(select third long mess
...
limit 0, ?)

Putting the limit last would have been okay if it weren't for the
lack of a limit on the "first long mess" -- since, as I recall, you have
the same limit for "second" and "third".
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
O

odeits

        Well, I did generate that as a bit of off-the-cuff...

        Apparently SQL parses the UNION as a higher precedence than LIMIT --
wanting it to apply to the final results of everything.

        And since I'm trying to reduce the number of selects overall, I sure
don't want to suggest making them subselects...

select first long mess
...
union
select * from
        (select second long mess
                ...
                limit 0, ?)
union
select * from
        (select third long mess
                ...
                limit 0, ?)

        Putting the limit last would have been okay if it weren't for the
lack of a limit on the "first long mess" -- since, as I recall, you have
the same limit for "second" and "third".
--
        Wulfraed        Dennis Lee Bieber               KD6MOG
        (e-mail address removed)              (e-mail address removed)
                HTTP://wlfraed.home.netcom.com/
        (Bestiaria Support Staff:               (e-mail address removed))
                HTTP://www.bestiaria.com/

Doing all of this work in the query made me realize that all the
filtering can be done just on the ADS table, so i modified the query
you provided to this :

select adid, rundateid,priority, rundate, ni,city,state
FROM ads NATURAL JOIN rundates NATURAL JOIN newspapers WHERE adid in

(
SELECT * from (
SELECT adid from ads
where status in (1, 3) and user
= :USER LIMIT 0, :STACK
)

UNION
SELECT * from (
SELECT adid from ads
where status = 1 and time <
datetime("now", "-%d minutes") LIMIT 0,:STACK
)


UNION
SELECT * from (
SELECT adid from ads
where status in (0, 2) and
priority in
(
select priority from
users natural join groups where user = :USER
) limit 0,:STACK
)
)

order by status desc, priority, time, adid
limit 0, :STACK

and i have achieved a 4x improvement in speed!!! thanks so much.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,738
Latest member
JinaMacvit

Latest Threads

Top