Davide Muzzarelli » sviluppo software /blog L'informatica a valore aggiunto Mon, 25 May 2015 22:41:52 +0000 en hourly 1 http://wordpress.org/?v=3.3.2 Dynamic pure SQL WHERE clauses and optional JOINs /blog/2015/01/dynamic-pure-sql-where-clauses-and-optional-joins/ /blog/2015/01/dynamic-pure-sql-where-clauses-and-optional-joins/#comments Tue, 06 Jan 2015 02:44:54 +0000 Davide Muzzarelli /blog/?p=332 Update 2015-05-25: see the video tutorial at the end of this article.

A dynamic SQL query is a query that operates accordingly through parameters.

Sometimes it is necessary to query the database filtering the rows in base at the user input. Like in a search, an user would choose several filters and not others.

The examples are in PostgreSQL and Python, but can be easily adapted to any database and language.

Imagine a table of customers:

CREATE TABLE "customers" (
    "id" serial PRIMARY KEY,
    "name" varchar(64) NOT NULL,
    "active" bool NOT NULL,
    "country" varchar(32)
);

The classic approach

The classic approach is to compose the SQL joining strings and conditional constructs, for example:

# Function that performs the query
# The parameters have null defaults
def customers_filtered(active=None, country=None):
    # This is the beginning of the query
    query = '
        SELECT *
        FROM customers
        WHERE'

    filters = []

    # Add the active filter, if not null
    if active is not None:
        filters.append('active=%(active)s')

    # Add the country filter, if not null
    if country is not None:
        filters.append('country=%(country)s')

    # Join all the filter queries chunks with an AND
    query += ' AND '.join(filters)

    # Prepare the parameters
    parameters = {
        'active': active,
        'country': country
    }

    # Execute the query and return the results
    return execute(query, parameters)

The resulting code is long, the query takes times to write and is unclear to read.

The database have to make an execution plan before executing the query. The execution plan is re-used when the SQL query is static, but this query is dynamic so the execution plan will be not re-used a second time and making the query a bit slower.

Optional WHERE clause

There is a solution with a negligible impact on perfomances, much easier to write and to read. The trick is to use NULL conditionals, so the query will be:

# Function that performs the query
# The parameters have the null defaults
def customers_filtered(active=None, country=None):
    # This is the full query!
    query = '
        SELECT *
        FROM customers
        WHERE (%(active)s IS NULL OR active=%(active)s)
              AND (%(country)s IS NULL OR country=%(country)s)'

    # Prepare the parameters
    parameters = {
        'active': active,
        'country': country
    }

    # Execute the query and return the results
    return execute(query, parameters)

The trick is to validate the right part of the OR clause only if the parameter is not NULL.

Optional JOINs

A similar approach can be applied for the JOIN clauses. First, create the “orders” table:

CREATE TABLE "orders" (
    "id" serial PRIMARY KEY,
    "customer_id" int NOT NULL,
    "date" timestamp NOT NULL,
    "value" int NOT NULL
);

It is possible to use a dynamic JOIN in order to get or not expensive data to calculate. The following SUM will be performed only if needed, setting the “flag” as true:

SELECT u.id, u.name, u.active, SUM(o.value) AS total
FROM customers AS u
LEFT JOIN orders AS o ON (true=%(flag)s AND o.customer_id=u.id)
GROUP BY u.id

Also in this case the performance impact is risible and the query remains very clear to read.

Conclusions

The classic method of composing the queries can be a source of bugs and headaches, this should be avoided. These solutions can reduce the code and increase the readability, especially when the ORM cannot be used or with the CQRS pattern (Command Query Responsibility Separation).

A video tutorial

Nat Dunn has created a nice video tutorial inspired by this article, it is part of his Python course:

Discussion on
Hacker News: https://news.ycombinator.com/item?id=8842677
Reddit: http://www.reddit.com/r/Database/comments/2rh0ak/dynamic_pure_sql_where_clauses_and_optional_joins/
Twitter: https://twitter.com/davmuz_en/status/552295916385087489

]]>
/blog/2015/01/dynamic-pure-sql-where-clauses-and-optional-joins/feed/ 0
Two template filters for Django /blog/2012/01/two-template-filters-for-django/ /blog/2012/01/two-template-filters-for-django/#comments Fri, 20 Jan 2012 11:46:23 +0000 Davide Muzzarelli /blog/?p=254 I want to signal two new template filters for Django.

Precise truncate words by chars

It truncates the text when it exceeds a certain number of characters and deletes the last word. Adds ‘…’ at the end of the text, only if truncated.

The difference between other similar filters is that this deletes the last word only if partial, so it is more precise and pleasant.

The snippet is here.

Divide a list into exact columns

It divides a list into an exact number of columns.

The difference between other similar filters it that the number of columns is guaranteed, even if there are not enough elements to fill all the columns.

The snippet is here.

]]>
/blog/2012/01/two-template-filters-for-django/feed/ 0
Convertire un repository SVN ad uno GIT in remoto /blog/2009/02/convertire-un-repository-svn-ad-uno-git-in-remoto/ /blog/2009/02/convertire-un-repository-svn-ad-uno-git-in-remoto/#comments Sat, 28 Feb 2009 17:29:00 +0000 Davide Muzzarelli /blog/2009/02/convertire-un-repository-svn-ad-uno-git-in-remoto/ Recentemente ho convertito i miei repository Subversion verso GIT.
La conversione in parte è dolorosa perché GIT è più difficile e complicato da usare rispetto a SVN ma offre un paio di vantaggi di cui ho bisogno: ottimizzazione della banda (possibilità di lavorare offline), migliore gestione dei branch. Prima o poi scriverò un post a riguardo dei vantaggi/svantaggi.

Convertire un repository non è una cosa così immediata, mi ci sono volute molte prove e la documentazione di GIT non è poi così completa. La maggior parte degli esempi che si trova in rete si riferisce all’uso di SVN con GIT, o all’importazione di un repository Subversin in locale e non ad una conversione completa in remoto.

Ecco le condizioni:

  1. Convertire un repository SVN ad uno in GIT, eliminando quindi il vecchio repository alla fine del passaggio.
  2. Il repository GIT deve trovarsi in un server remoto.
  3. I tag e i branch vanno convertiti correttamente nei corrispettivi in GIT.
  4. Il “trunk” deve diventare il “master”.
  5. Il repository Subversion da copiare si trova all’indirizzo svn://login1@server1/repository/progetto1/
  6. Il repository di destinazione GIT, già pronto ad accogliere i nuovi dati, si trova all’indirizzo git://login2@server2/repository/progetto2.git
  7. I comandi eseguiti non devono avere nessun effetto sul vecchio repository, in modo da poter ripetere la procedura da capo se qualcosa va storto.

Dopo aver provato la versione 1.5.2 di GIT, ho capito che non era adatta per questo lavoro dato che non era ancora pronta per via di qualche bug. Sono quindi passato alla versione 1.6.3, ma già la versione 1.5.3 potrebbe andare bene.

Il primo passaggio è quello di clonare in locale il primo repository sul nostro computer:

git svn clone -s svn://login1@server1/repository/progetto1/ progetto

Il parametro “-s” indica di utilizzare i nomi di default per “trunk”, “branches” e “tags”, in modo da individuare le directory giuste. E’ possibile sovrascriverli con i parametri “–trunk”, “–branches” e “–tags”.
L’ultimo parametro “progetto” è il nome della directory da creare in locale.
Una volta eseguito questo comando, git-svn inizierà a scaricare i dati nella directory “progetto”.

Una volta scaricato tutto, indichiamo il repository di destinazione:

cd progettogit remote add origin git://login2@server2/repository/progetto2.git

git-svn non converte direttamente i tag e i branch, ma crea solo dei branch in locale. Vediamo quali tag e quali branch abbiamo da convertire:

git branch -a

Mettiamo che il risultato sia questo:

* master miobranch altrobranch tags/1.0 tags/2.0 trunk

Il parametro “-a” serve a mostrare tutti i branch e non solo quelli locali.
Il branch master non contiene il trunk, ma il branch con l’ultimo commit, per cui sarà da correggere.

Siamo pronti per convertire i tag:

git tag 1.0 tags/1.0git branch -r -d tags/1.0git tag 2.0 tags/2.0git branch -r -d tags/2.0

Il primo comando crea un tag GIT utilizzando il riferimento a quello vecchio (che è in remoto).
Il secondo comando elimina il branch del vecchio tag (opzione “-d”) che si trova in remoto (“-r”).
Il terzo ed il quarto comando eseguono la stessa procedura sul secondo tag.

Ora passiamo alla conversione dei branch:

git checkout -b mionuovobranch miobranchgit checkout -b altrobranch altrobranchgit checkout trunk trunk

Il primo comando converte il branch “miobranch” in “mionuovobranch” cambiandone anche il nome.
Il secondo comando converte il branch “altrobranch” mantenendo lo stesso nome. Questo genererà un errore a cui non bisogna fare caso, proprio perché il vecchio branch ha lo stesso nome di quello nuovo.
Il secondo comando serve a preparare il trunk per sostituirlo al master.

Sostituiamo il master con il trunk:

git branch -D mastergit checkout trunkgit checkout -f -b mastergit branch -d trunk

Il primo comando elimina il master con tutta la sua storia (parametro “-D”).
Il secondo comando ci fa spostare all’interno di trunk.
Il terzo comando crea il branch master basato su trunk (parametro “-b”) e ignora tutti gli eventuali errori (parametro “-f”).
Il quarto comando elimina il branch trunk mantenendo comunque lo storico (parametro “-d”).

Siamo pronti per immettere i dati sul nostro nuovo repository di destinazione:

git push origin mastergit push origin 1.0git push origin 2.0git push origin mionuovobranchgit push origin altrobranch

Il primo comando carica i dati sul repository remoto.
I comandi successivi aggiungono i tag e i branch.

A questo punto il lavoro è finito: possiamo eliminare la direcotory locale di GIT e creare una nuova copia locale pulita.

cd ..rm -fr progettogit clone git://login2@server2/repository/progetto2.git progetto
]]>
/blog/2009/02/convertire-un-repository-svn-ad-uno-git-in-remoto/feed/ 0
Come si diventa game developer? /blog/2008/05/come-si-diventa-game-developer/ /blog/2008/05/come-si-diventa-game-developer/#comments Fri, 23 May 2008 16:52:00 +0000 Davide Muzzarelli /blog/2008/05/come-si-diventa-game-developer/ Come si diventa game developer? A questa domanda noi di GPI abbiamo risposto su Punto-Informatico al seguente link.

]]>
/blog/2008/05/come-si-diventa-game-developer/feed/ 0
Sviluppo su IE, errore numero 80004004 /blog/2006/12/sviluppo-su-ie-errore-numero-80004004/ /blog/2006/12/sviluppo-su-ie-errore-numero-80004004/#comments Tue, 12 Dec 2006 17:50:00 +0000 Davide Muzzarelli /blog/2006/12/sviluppo-su-ie-errore-numero-80004004/ Come sempre Internet Explorer tortura sviluppatori e utenti fino all’ultimo con messaggi incomprensibili ed errori senza senso.

Questa è la volta dell’errore numero 80004004, la documentazione della Microsoft è carente sulle cause anche perché questo errore sembra essere attribuibile a più tipi di problema.

Il bug affligge le seguenti versioni di Internet Explorer, che sono praticamente tutte quelle degli ultimi dieci anni e passa:

  • Microsoft Internet Explorer 3.0
  • Microsoft Internet Explorer 3.01
  • Microsoft Internet Explorer 3.02
  • Microsoft Internet Explorer 4.0 128-Bit Edition
  • Microsoft Internet Explorer 4.01 Service Pack 2
  • Microsoft Internet Explorer 4.01 Service Pack 1
  • Microsoft Internet Explorer 4.01 Service Pack 2
  • Microsoft Internet Explorer 5.0
  • Microsoft Internet Explorer 5.01
  • Microsoft Internet Explorer 5.5
  • Microsoft Visual Basic, Scripting Edition 4.0
  • Microsoft Visual Basic, Scripting Edition 5.0
  • Microsoft JScript 4.0
  • Microsoft JScript 5.0
  • Microsoft Visual Basic 6.0 Learning Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic 6.0 Enterprise Edition
  • Microsoft Visual Basic 6.0 Enterprise Edition Service Pack 3

A volte la Microsoft lo attribuisce a Visual Basic, altre volte al suo Java Script, altre volte ad ActiveX; io credo invece che il problema riguardi una libreria in comune tra questi linguaggi.

In particolare, nello sviluppo web, il problema nasce dal fatto di cercare un elemento contenuto in una tabella o in un DIV nello stesso punto in cui si trova questo elemento.

L’errore viene generato con uno script del genere:

...<table>    <tr>        <td>            <div id="foo" class="bar">foobar</div>            <script language="javascript">                var obj = document.getElementById('foo');                var classes = obj.getAttribute('class');            </script>        </td>    </tr>...

Come si può vedere lo script è contenuto ed eseguito all’interno della stessa cella e tabella dell’elemento da cercare, questo solleva l’errore.

Attenzione a chi usa le API di Google, in particolare Google Maps, molte persone hanno riscontrato lo stesso errore senza aver trovato una soluzione (quella che ho appena suggerito). E’ per tale problema che Google consiglia di caricare gli script con un <body onload=”…”>, anche se in alcuni casi questo non è proprio possibile.

Per risolvere è sufficiente lasciare chiudere la tabella eseguendo lo script al termine della pagina. Nel caso in cui non si possa trovare un’alternativa il problema ricade ancora una volta sulle spalle dello sviluppatore che dovrà eliminare una funzionalità del proprio sito a svantaggio dell’utente. Grazie Microsoft…

PS: in alcuni casi l’errore non viene segnalato esplicitamente da Internet Explorer, bisogna provare con un’altra versione del browser o da un’altra macchina.

]]>
/blog/2006/12/sviluppo-su-ie-errore-numero-80004004/feed/ 8
Gestire i file di configurazione con SVN /blog/2006/11/gestire-i-file-di-configurazione-con-svn/ /blog/2006/11/gestire-i-file-di-configurazione-con-svn/#comments Sat, 11 Nov 2006 18:42:00 +0000 Davide Muzzarelli /blog/2006/11/gestire-i-file-di-configurazione-con-svn/ I progetti software hanno quasi sempre uno o più file di configurazione che sono personalizzati in base al contesto. Ogni sviluppatore può avere un file di configurazione personale e anche i server di test e di produzione ne hanno uno creato apposta.

Avendo un sistema di repository, come Subversion (aka: SNV), i file di configurazione sono gestiti come normali file e quindi inviati nella fase di commit. Ogni contesto (copia di lavoro o server) ha una sua personalizzazione del file che verrebbe sovrascritta dall’ultimo commit effettuato. Questo è un problema comune in quanto le configurazioni sono continuamente riscritte e sovrascritte, dovendo essere corrette ogni qual volta che si aggiorna il codice, il che comporta parecchio lavoro in più.

Si può risolvere il problema utilizzando l’opzione svn:ignore di SVN che permette di ignorare i file indicati, in questo caso i file di configurazione. Per mantenere comunque una base dei file ignorati è sufficiente creare dei i file di configurazione generici aggiungendo un’estensione alla fine.

Nel caso di esempio, in cui ho un file di nome config.ini, creo una copia con:

svn mv config.ini config.ini.tmpl

Con questo comando ho rinominato il file mantenendone intatta la storia delle precedenti revisioni. Ho scelto di aggiungere l’estensione tmpl per indicare che il file è un template (o modello) dell’originale, ma ognuno può utilizzare l’estensione o il nome che preferisce.

Per ignorare il file mi sposto all’interno della directory dove risiede e lancio il seguente comando:

svn propedit svn:ignore .

Questo comando mi permette di modificare la lista dei file da ignorare per la directory in cui mi trovo ( . ). Una volta eseguito il comando si apre l’editor di testo in cui si possono inserire i nomi dei file, uno per riga. E’ anche possibile usare i caratteri wildcard, come gli asterischi, per indicare contemporaneamente più file.

Una volta fatto posso inviare le modifiche:

svn ci -m"Da ora in poi potete usare i vostri file di configurazione senza rischi!"svn up

Da questo punto in poi il file config.ini è ignorato e posso procedere con il mio lavoro: creo una semplice copia del file di template config.ini.tmpl (senza usare SVN!) dandogli il nome config.ini e lo modifico secondo i miei bisogni. Al prossimo aggiornamento il file config.ini non sarà toccato e tutti i miei collaboratori potranno avere i loro file di configurazione senza creare problemi agli altri.

Nel caso in cui si debbano inserire delle modifiche utili a tutti gli sviluppatori è sufficiente modificare il solo template e ricreare il proprio file di configurazione personalizzato.

Il sistema funziona anche con link e directory aprendo la strada a diverse possibilità.

]]>
/blog/2006/11/gestire-i-file-di-configurazione-con-svn/feed/ 0