#🔒 I have a big database, my script does thousands of queries, need them to be fast

91 messages · Page 1 of 1 (latest)

wild hornetBOT
#

@nova crescent

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

nova crescent
#

amazing I have to write everyuthig again because of the bot

clever obsidian
#
My db is around 150gb, I'm using postgresql, I will share my db setup.

Initially I need to iterate over every event, then for each event, one more query regarding odds (lets use full time result table)

I was querying all events in a given timeframe, then iterating over them and querying for odds in each iteration, but this took a lot of time.

The whole process had an estimated of 5 hours
nova crescent
#

!paste

wild hornetBOT
#
Pasting large amounts of code

If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/

After pasting your code, save it by clicking the Paste! button in the bottom left, or by pressing CTRL + S. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.

nova crescent
#

Before, with sqlite, I was doing

# self.database.fetch_closing_odds
def fetch_closing_odds(self, league_ids=None, books=None):
    """
    Recupera las cuotas de cierre de los eventos, opcionalmente filtradas por ligas y casas de apuestas.

    Parameters:
    - league_ids (list[int]): Lista de IDs de ligas para filtrar eventos (opcional).
    - books (list[str]): Lista de nombres de casas de apuestas para filtrar cuotas (opcional).

    Returns:
    - list[dict]: Lista de diccionarios con las cuotas de cierre.
    """
    query = """
        SELECT
            ftr.event_id,
            ftr.home_odd,
            ftr.draw_odd,
            ftr.away_odd,
            ftr.source
        FROM full_time_result ftr
        JOIN (
            SELECT event_id, source, MAX(add_time) AS max_add_time
            FROM full_time_result
            GROUP BY event_id, source
        ) latest
        ON ftr.event_id = latest.event_id AND ftr.source = latest.source AND ftr.add_time = latest.max_add_time
    """
    conditions = []
    params = []

    if league_ids:
        conditions.append(
            "ftr.event_id IN (SELECT id FROM events WHERE league_id IN ({}))".format(
                ",".join("?" for _ in league_ids)
            )
        )
        params.extend(league_ids)

    if books:
        conditions.append("ftr.source IN ({})".format(",".join("?" for _ in books)))
        params.extend(books)

    if conditions:
        query += " WHERE " + " AND ".join(conditions)

    self.cursor.execute(query, params)
    rows = self.cursor.fetchall()

    return [
        {
            "event_id": row[0],
            "home_odd": row[1],
            "draw_odd": row[2],
            "away_odd": row[3],
            "source": row[4],
        }
        for row in rows
    ]

# self.database.fetch_events_info
def fetch_events_info(self, start_ts, end_ts):
    """
    Devuelve filas (id, home_score, away_score, time) de 'events',
    filtradas por start_ts y end_ts si están dados.
    """
    query = "SELECT id, home_score, away_score, time FROM events"
    conditions = []
    params = []
    if start_ts is not None:
        conditions.append("time >= ?")
        params.append(start_ts)
    if end_ts is not None:
        conditions.append("time <= ?")
        params.append(end_ts)
    if conditions:
        query += " WHERE " + " AND ".join(conditions)

    self.database.cursor.execute(query, params)
    return self.database.cursor.fetchall()



# Calculate Alphas
odds_data = self.database.fetch_closing_odds(league_ids = league_ids, books = sources)

events_info = self._fetch_events_info(start_time, end_time)
event_data = {row[0]: row for row in events_info}

for row in odds_data:
  # Perform calculations
  ...

and this took like... 15 mins

proud lion
#

if you paste your database schema (the table create commands), it will help

proud lion
#

oh, sorry I missed that

nova crescent
#

np 🙂

proud lion
#

have you tried analyzing the queries yet?

#

EXPLAIN SELECT ...

#

you'll want to make sure there are no unnecessary sequential scans there

nova crescent
#

never tried that

harsh summit
#

maybe do some of the compairisons inside the db?

proud lion
#

how can this work anyway? the events table does not have a column named time according to your schema

#

why do you have a multi-column PK in events?

nova crescent
#

right, I went from sqlite to postgresql, now time (which was an int ) is event_time (which is now a naive datetime.datetime object)

proud lion
#

but why did you rename the column?

#

and why the multi-column pk?

nova crescent
#

idk tbh

#

cause I had to partition odds tables

#

well I didnt had to, I decided to

proud lion
#

to what end?

nova crescent
#

because odds have like 1 000 000 000 rows

proud lion
#

and what does the partitioning have to do with either?

nova crescent
#

and by partitioning , queries might be faster?

#

you asked why the multi column pk

proud lion
#

yes

#

I still don't see an answer to that

nova crescent
#

I made a partition based on event time

#

and to partition based on event time, event time needs to be the PK

#

or part of a multi column pk

#

well the thing is, I dont care about event time as pk

#

I do care about id as pk

#

hencethe multicolumn pk

proud lion
#

ok so which query seems slow? is there a way for you to reduce the number of the queries?

harsh summit
#

could chuck quieries, could grab all and then chunk and use multiprocessing if running it singular is slow.

burnt lodge
#

I am not great at databases, but in case you think that you cannot optmize the queries anymore and they are still very slow, I recommend you checkout duck db, its good for fast complex queries and have good support for python

rugged carbon
#

@nova crescent you should look into joins and such constructs to do things inside the database instead of pulling the data out and looping over it from the program
this is why you should use relational databases

harsh summit
#

I still think running and pre-selection/case/match criteria the db offers will spped up processing as prefilter.

rugged carbon
#

and don't do premature optimization of the database unless you know exactly what is causing the slowness

rugged carbon
#

not fully anyway

#

and you should be optimizing the database after you have analyzed the execution plan

nova crescent
#

Ive no clue what optimization is about tbh

rugged carbon
nova crescent
#

this is a personal project, and I dont have a contact for that

rugged carbon
nova crescent
#

I can share the db

rugged carbon
#

but i'm a bit surprised of how you have amassed that amount of data in your database for a personal project

#

@nova crescent i do know that there is some really good database people hanging out in #databases

nova crescent
#

Been pulling data for a week

#

It might end up around 400GB when Im done downloading

#

I'll need queries to be fast, cant wait a week for calculations 😄

nova crescent
#

so what are your recommendations for my situation?

rugged carbon
nova crescent
#

10 months worth of queries was like 5 hours eta, imagine 4 years

rugged carbon
rugged carbon
nova crescent
#

Not home, once Im back Ill use the rewt of the day to try and understand the problem Im facing

#

Ill send more info here when I have it

rugged carbon
#

@nova crescent what kind of disk is your database stored in and how much ram do you have?

rugged carbon
# nova crescent 32gb, hdd

if that is spinning 7200 rpm or maybe even lower i can definitely see that being slow compared to a ssd, or better yet, a high performance nvme drive

nova crescent
#

but my previous sqlite db was also in my hdd

rugged carbon
nova crescent
#

Sqlite was 80gb

#

And my current postgres is around 100

#

yeah

rugged carbon
nova crescent
#

if it handle teras, it shpuld be able to handle gbs

#

I mean Im sure I can reduce the execution time at least to sqlite level

#

Im also sure my db struct, config and hw could be better

rugged carbon
nova crescent
#

I also agree, and I dont have that, but it has to be possibleeee 😭

#

If sqlite was fast enough...

#

Unless I just copy everything into a sqlite db lol

rugged carbon
nova crescent
#

Yeah Im defo available

rugged carbon
#

starting with explain

nova crescent
#

Tbh Ive been using o1 A LOT for this, it would have been a long lasting time project, it ended up being one though, and still I have a lot to do (like performance)

wild hornetBOT
#
Python help channel closed for inactivity

This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.