<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Stardata.it &#187; Tips&#8217;n&#039;Tricks</title>
	<atom:link href="http://www.stardata.it/category/tips/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.stardata.it</link>
	<description>Stardata - Database is our business</description>
	<lastBuildDate>Fri, 11 Sep 2009 13:13:06 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Quando &#8220;ENUM&#8221; fa i capricci</title>
		<link>http://www.stardata.it/quando-enum-fa-i-capricci-72/</link>
		<comments>http://www.stardata.it/quando-enum-fa-i-capricci-72/#comments</comments>
		<pubDate>Wed, 02 Sep 2009 18:50:19 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[ENUM]]></category>
		<category><![CDATA[row]]></category>
		<category><![CDATA[valori]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=72</guid>
		<description><![CDATA[Il tipo &#8220;ENUM&#8221; consente di definire una lista di valori da attribuire a un campo, una soluzione molto comoda quando i valori sono pochi e vogliamo evitare la creazione di una tabella di supporto. Vediamo però un esempio in cui le cose non vanno come dovrebbero.

mysql> CREATE TABLE test_enum (ID INT, strano ENUM('0','1'));
Query OK, 0 [...]]]></description>
			<content:encoded><![CDATA[<p>Il tipo &#8220;ENUM&#8221; consente di definire una lista di valori da attribuire a un campo, una soluzione molto comoda quando i valori sono pochi e vogliamo evitare la creazione di una tabella di supporto. Vediamo però un esempio in cui le cose non vanno come dovrebbero.</p>
<p><code><br />
mysql> CREATE TABLE test_enum (ID INT, strano ENUM('0','1'));<br />
Query OK, 0 rows affected (0.38 sec)<br />
</code></p>
<p>Abbiamo creato una tabella con un campo ENUM che accetta i valori &#8216;0&#8242; e &#8216;1&#8242;. Fin qui, tutto bene.</p>
<p><code><br />
mysql> INSERT INTO test_enum VALUES (1,1);<br />
Query OK, 1 row affected (0.28 sec)<br />
</code></p>
<p>Il primo inserimento dovrebbe fare quel che ci aspettiamo, cioè mettere &#8216;1&#8242; nel campo ID e &#8216;1&#8242; nel campo strano. Controlliamo:</p>
<p><code><br />
mysql> SELECT * FROM test_enum;<br />
+------+--------+<br />
| ID   | strano |<br />
+------+--------+<br />
|    1 | 0      |<br />
+------+--------+<br />
1 row in set (0.00 sec)<br />
</code></p>
<p>Uhm, no. Questo non è quel che volevamo. Come mai il campo strano ha uno zero mentre noi abbiamo inserito 1? Il motivo è che i valori di enum devono essere valori testuali e quindi andrebbero messi fra virgolette.</p>
<p>Va bene, ma cosa succede se invece mettiamo un numero? Succede che MySQL cerca di venirci incontro, interpretando il numero come &#8220;il valore che sta alla posizione indicata da quel numero&#8221;. Nel nostro caso, si trattava di 1, che identifica la prima posizione, che nel nostro caso era &#8216;0&#8242;. Ecco spiegato l&#8217;arcano. Questo meccanismo si rivela comodo quando i valori hanno una grafia dubbia, e quindi per evitare errori possiamo citarli per posizione invece che per nome:</p>
<p><code><br />
nano ENUM('Dotto',  'Gongolo', 'Eolo', 'Brontolo', 'Mammolo', 'Pisolo', 'Cucciolo');<br />
</code></p>
<p>Ora l&#8217;istruzione</p>
<p><code><br />
INSERT INTO nani set nano = 1<br />
</code></p>
<p>farà sì che il campo nano contenga &#8216;Dotto&#8217;, cioè il primo valore della serie.</p>
<p>Un tranello simile ci attende quando vogliamo utilizzare il valore di default di un campo. Sapete che ogni campo di una tabella MySQL può avere un valore di default, da usare quando si omette quella colonna in un inserimento.</p>
<p>Guardate la creazione di questa tabella, in cui mettiamo un valore di default per un campo ENUM.</p>
<p><code><br />
mysql> CREATE TABLE test_enum2 (ID int, strano enum('0','1') NOT NULL DEFAULT 1 );<br />
Query OK, 0 rows affected (0.01 sec)<br />
</code></p>
<p>Ora, se inseriamo un record che non contiene un valore per il campo &#8220;strano&#8221;, ci potremmo aspettare che tale campo assuma il valore &#8216;1&#8242;.</p>
<p><code><br />
mysql> INSERT INTO test_enum2 (ID) VALUES (10);<br />
Query OK, 1 row affected (0.00 sec)<br />
</code></p>
<p>Invece, anche questa volta, ci troviamo il valore &#8216;0&#8242;.</p>
<p><code><br />
mysql> SELECT * FROM test_enum2;<br />
+------+--------+<br />
| ID   | strano |<br />
+------+--------+<br />
|   10 | 0      |<br />
+------+--------+<br />
1 row in set (0.00 sec)<br />
</code></p>
<p>La spiegazione è la stessa. Anche per la definizione del valore di default, indicando un numero senza virgolette si intende la posizione nella lista, non il valore stesso.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/quando-enum-fa-i-capricci-72/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Trovare ed eliminare duplicati</title>
		<link>http://www.stardata.it/trovare-ed-eliminare-duplicati-58/</link>
		<comments>http://www.stardata.it/trovare-ed-eliminare-duplicati-58/#comments</comments>
		<pubDate>Wed, 02 Sep 2009 18:07:58 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[distinct]]></category>
		<category><![CDATA[duplicati]]></category>
		<category><![CDATA[elenco]]></category>
		<category><![CDATA[having]]></category>
		<category><![CDATA[record]]></category>
		<category><![CDATA[where]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=58</guid>
		<description><![CDATA[La duplicazione di dati non dovrebbe mai avvenire in un database bene organizzato, ma talvolta, quando si ha a che fare con dati ereditati da applicazioni poco accurate, può accadere di ritrovarsi con una tabella che contiene record duplicati.
In questa poco piacevole situazione, ci sono tre tipi di operazioni che si vorrebbero eseguire:

Elencare i record [...]]]></description>
			<content:encoded><![CDATA[<p>La duplicazione di dati non dovrebbe mai avvenire in un database bene organizzato, ma talvolta, quando si ha a che fare con dati ereditati da applicazioni poco accurate, può accadere di ritrovarsi con una tabella che contiene record duplicati.</p>
<p>In questa poco piacevole situazione, ci sono tre tipi di operazioni che si vorrebbero eseguire:</p>
<ul>
<li>Elencare i record senza duplicati;</li>
<li>Elencare i valori duplicati;</li>
<li>Eliminare i duplicati.</li>
</ul>
<p>Prendiamo ad esempio una tabella con qualche record duplicato (per esigenze di spazio, la facciamo corta, ma supponete di avere svariate migliaia di record).</p>
<p><code><br />
mysql> SELECT * FROM condoppi;<br />
+---+------+-----+<br />
| i | a    | b   |<br />
+---+------+-----+<br />
| 1 | aaa  | xxx |<br />
| 2 | bbb  | yyy |<br />
| 3 | aaa  | yyy |<br />
| 4 | ccc  | www |<br />
| 5 | aaa  | xxx |<br />
| 6 | ddd  | zzz |<br />
| 7 | aaa  | xxx |<br />
| 8 | bbb  | yyy |<br />
| 9 | ccc  | www |<br />
+---+------+-----+<br />
9 rows in set (0.18 sec)<br />
</code></p>
<p>La prima esigenza, vedere i record senza doppi, viene risolta facilmente usando la clausola DISTINCT:</p>
<p><code><br />
mysql> SELECT DISTINCT a, b FROM condoppi;<br />
+------+-----+<br />
| a    | b   |<br />
+------+-----+<br />
| aaa  | xxx |<br />
| aaa  | yyy |<br />
| bbb  | yyy |<br />
| ccc  | www |<br />
| ddd  | zzz |<br />
+------+-----+<br />
5 rows in set (0.12 sec)<br />
</code></p>
<p>Facilissimo. Basta premettere DISTINCT alla lista dei campi per avere un elenco pulito, senza duplicati.</p>
<p>Notate che se avessi inserito fra i campi anche la chiave primaria (campo &#8220;i&#8221;), il DBMS mi avrebbe stampato tutti i record, perché ovviamente sono differenziati grazie al campo univoco. La clausola DISTINCT è anche utile per avere un elenco dei valori distinti (appunto) per una data colonna. Per la seconda esigenza, vedere quali valori sono doppiati, non c&#8217;è formula magica immediata, ma bisogna sforzarsi un po&#8217; di più.</p>
<p>Il metodo consiste nel raggruppare i valori con un conteggio, e considerare solo quelli maggiori di 1. Si noti la clausola HAVING che (a differenza di WHERE) viene valutata dopo l&#8217;operazone di raggruppamento.</p>
<p><code><br />
mysql> SELECT a, b COUNT(*) AS quanti<br />
       FROM condoppi<br />
       GROUP BY a, b<br />
       HAVING quanti > 1;<br />
+------+-----+--------+<br />
| a    | b   | quanti |<br />
+------+-----+--------+<br />
| aaa  | xxx |      2 |<br />
| bbb  | yyy |      2 |<br />
| ccc  | www |      2 |<br />
+------+-----+--------+<br />
3 rows in set (0.39 sec)<br />
</code></p>
<p>Così possiamo vedere quali sono i valori (o le combinazioni di valori) che sono registrati più di una volta.</p>
<p>Infine, se vogliamo cancellare dalla tabella i valori doppi, esiste un metodo molto semplice e pratico: aggiungere un indice univoco alla tabella, ma usando la clausola IGNORE, per far sì che il DBMS non si fermi al primo errore di record duplicato, ma vada avanti, di fatto saltando l&#8217;inserimento dei record con valori già inseriti.</p>
<p><code><br />
mysql> ALTER IGNORE TABLE condoppi ADD UNIQUE KEY (a, b) ;<br />
Query OK, 9 rows affected (0.54 sec)<br />
Records: 9  Duplicati: 4  Avvertimenti: 0<br />
mysql> SELECT * FROM condoppi;<br />
+---+------+-----+<br />
| i | a    | b   |<br />
+---+------+-----+<br />
| 1 | aaa  | xxx |<br />
| 2 | bbb  | yyy |<br />
| 3 | aaa  | yyy |<br />
| 4 | ccc  | www |<br />
| 6 | ddd  | zzz |<br />
+---+------+-----+<br />
5 rows in set (0.02 sec)<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/trovare-ed-eliminare-duplicati-58/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Aggiornare più condizioni con una sola query</title>
		<link>http://www.stardata.it/aggiornare-piu-condizioni-con-una-sola-query-52/</link>
		<comments>http://www.stardata.it/aggiornare-piu-condizioni-con-una-sola-query-52/#comments</comments>
		<pubDate>Wed, 02 Sep 2009 17:54:54 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[CASE]]></category>
		<category><![CDATA[record]]></category>
		<category><![CDATA[UPDATE]]></category>
		<category><![CDATA[where]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=52</guid>
		<description><![CDATA[PW scrive che vorrebbe trovare un modo di eseguire diversi UPDATE su una tabella usando una sola query. Nella pratica, si trova ad avere record di prodotti di diverse aziende, che vuole aggiornare in maniera omogenea, ma senza dover ripetere la stessa query diverse volte.
Per esempio, vorrebbe cambiare lo sconto offerta sui libri di informatica, [...]]]></description>
			<content:encoded><![CDATA[<p>PW scrive che vorrebbe trovare un modo di eseguire diversi UPDATE su una tabella usando una sola query. Nella pratica, si trova ad avere record di prodotti di diverse aziende, che vuole aggiornare in maniera omogenea, ma senza dover ripetere la stessa query diverse volte.</p>
<p>Per esempio, vorrebbe cambiare lo sconto offerta sui libri di informatica, di finanze, di attualità, con quantità differenti per ogni categoria. Una possibilità è la seguente:<br />
<code><br />
UPDATE libri<br />
SET sconto =<br />
CASE<br />
WHEN sezione = 'informatica' THEN 12<br />
WHEN sezione = 'attualita'   THEN 10<br />
WHEN sezione = 'finanze'     THEN 8<br />
ELSE sconto<br />
END<br />
WHERE id_editore IN (5,6,8,11);<br />
</code></p>
<p>Questa query agisce solo sui libri degli editori considerati (clausola WHERE) e al loro interno modifica il campo sconto solo nelle sezioni indicate. Per le altre sezioni, il valore viene lasciato uguale, grazie alla clausola ELSE della funzione CASE.</p>
<p>Comporre query di questo tipo può essere più difficile che comporre una query per ogni condizione, ma talora si guadagna in efficienza, ed è bene sapere che ci sono alternative!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/aggiornare-piu-condizioni-con-una-sola-query-52/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Passaggio efficiente di record tra client e server</title>
		<link>http://www.stardata.it/passaggio-efficiente-di-record-tra-client-e-server-81/</link>
		<comments>http://www.stardata.it/passaggio-efficiente-di-record-tra-client-e-server-81/#comments</comments>
		<pubDate>Sun, 02 Aug 2009 19:05:34 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[count]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=81</guid>
		<description><![CDATA[In un protocollo client-server, quando viene eseguita una query, il server ha due modi per inviare i record trovati al client: uno alla volta o tutti insieme. Il modo effettivamente usato dipende dalle richieste del client.
Programmando MySQL con un linguaggio evoluto come Perl o PHP, la differenza non si coglie, perché il modo di ricezione [...]]]></description>
			<content:encoded><![CDATA[<p>In un protocollo client-server, quando viene eseguita una query, il server ha due modi per inviare i record trovati al client: uno alla volta o tutti insieme. Il modo effettivamente usato dipende dalle richieste del client.</p>
<p>Programmando MySQL con un linguaggio evoluto come Perl o PHP, la differenza non si coglie, perché il modo di ricezione attivato per default è il &#8220;tutti insieme&#8221;, cioè il server invia i record in un unico array che viene quindi trasferito alla memoria del client.</p>
<p>Chi si è preso la briga di guardare dietro le quinte del driver usato da Perl e PHP, però, ha visto che questi linguaggi si appoggiano alla libreria di funzioni scritta in C, che ha la capacità di differenziare il tipo di invio dei record. La libreria delle API (Application Programming Interface) di MySQL ha due funzioni per questo compito: mysql_use_result e mysql_store_result.</p>
<p><code>mysql_store_result</code> è il comportamento più comodo per il programmatore, perché trasporta tutti i record ritrovati in una sola mandata. In questo modo il programma ha a disposizione due funzionalità che di solito si danno per scotate, ma non lo sono: il numero di record ritrovati e la possibilità di saltare avanti e indietro nella lista dei record.</p>
<p><code>mysql_use_result</code> invece istruisce il server perché invii i record uno alla volta. Questo comportamento, che a prima vista potrebbe sembrare illogico e indesiderabile, trova fondamento nei casi in cui si debbano leggere grandi quantità di record, quando è meglio prendere i record uno per uno, farci quel che ci si deve fare (per esempio stamparlo o inviarlo a un file) e passare a quello successivo.</p>
<p>Poiché mandare i record tutti insieme richiede il costo aggiuntivo dell&#8217;occupazione di memoria per un array (la cui creazione richiede più tempo della semplice occupazione dello spazio di un singolo record) quando si manipolano grandi quantità di record (parlo di decine di MB), allora la gestione uno-alla-volta si dimostra più veloce.</p>
<p>Per contro, <code>mysql_use_result</code> ha lo svantaggio di non poter dire quanti record sono stati trovati finché tutti sono stati trasferiti dal server al client.</p>
<p>Comunque, per quantità di record limitate (dipende anche dalle risorse della macchina) che coprono la maggior parte delle applicazioni, <code>mysql_store_result</code> è il metodo da preferirsi.</p>
<p>Ma, mi sento dire, se l&#8217;uso di queste funzioni si stabilisce in base al presunto numero di record e il programmatore non può conoscere il numero prima dell&#8217;esecuzione della query, com&#8217;è possibile prendere una decisione?</p>
<p>Potrei dire che i programmatori bravi sanno a priori se la query che stanno per eseguire è di quelle da tremila record o da tre milioni, ma a parte quelli che hanno queste intuizioni difficilmente trasferibili ad altri, se il vostro database ha una gran quantità di record e avete paura che un risultato ve ne porti qualche milione sul client, esaurendo tutta la memoria se usate <code>mysql_store_result</code>, potete sempre sondare il database con una query di conteggio.</p>
<p>Per esempio, prima di eseguire</p>
<p><code><br />
SELECT autore, titolo FROM libridiamazon WHERE autore LIKE "A%";<br />
</code></p>
<p>potete sondare il database con questa:</p>
<p><code><br />
SELECT COUNT(*) FROM libridiamazon WHERE autore LIKE "A%";<br />
</code></p>
<p>Se il conteggio è nell&#8217;ordine delle migliaia, usate <code>mysql_store_result</code>. Se invece si va oltre i diecimila, forse è il caso di scegliere <code>mysql_use_result</code>. Volete sapere come gestire questo doppio comportamento da Perl e PHP?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/passaggio-efficiente-di-record-tra-client-e-server-81/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lista record con numero progressivo</title>
		<link>http://www.stardata.it/lista-record-con-numero-progressivo-54/</link>
		<comments>http://www.stardata.it/lista-record-con-numero-progressivo-54/#comments</comments>
		<pubDate>Sun, 02 Aug 2009 17:58:17 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[counter]]></category>
		<category><![CDATA[elenco]]></category>
		<category><![CDATA[lista]]></category>
		<category><![CDATA[record]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=54</guid>
		<description><![CDATA[Questa richiesta è arrivata su un forum di programmazione: Come faccio a elencare i risultati di una query con un numero progressivo per ogni record?
Ci sono diverse risposte a questa domanda. La più immediata sarebbe di implementare il conteggio nell&#8217;ambito dell&#8217;applicazione che riceve i dati. Questa soluzione, però, richiede dettagli diversi in ogni linguaggio e [...]]]></description>
			<content:encoded><![CDATA[<p>Questa richiesta è arrivata su un forum di programmazione: <em>Come faccio a elencare i risultati di una query con un numero progressivo per ogni record</em>?</p>
<p>Ci sono diverse risposte a questa domanda. La più immediata sarebbe di implementare il conteggio nell&#8217;ambito dell&#8217;applicazione che riceve i dati. Questa soluzione, però, richiede dettagli diversi in ogni linguaggio e potrebbe non essere quel che si cerca.</p>
<p>Invece, esiste un modo per far contare i record a MySQL, in modo da avere un risultato che sia lo stesso, a prescindere dal linguaggio usato.</p>
<p>Forse non tutti sanno che MySQL consente di usare delle variabili &#8220;globali&#8221; che sono attive per tutta la durata di una connessione. Per esempio, un uso frequente di questa funzionalità si ha per ovviare alla mancanza di subquery nelle versioni inferiori alla 4.1:</p>
<p><code><br />
mysql> SELECT @massimo := max(stipendio) from paghe;<br />
mysql> SELECT nome, cognome, stipendio from paghe where stipendio = @massimo;<br />
</code></p>
<p>In maniera simile, possiamo usare una variabile per creare un contatore. Poiché ogni variabile ha valore NULL finché non le viene assegnato esplicitamente qualcosa, è necessario eseguire due query per poter raggiungere lo scopo. Prima, vediamo la query senza contatore.</p>
<p><code><br />
mysql> SELECT nome,stipendio, genere FROM persone;<br />
+--------+-----------+--------+<br />
| nome   | stipendio | genere |<br />
+--------+-----------+--------+<br />
| John   |      5000 | m      |<br />
| Mario  |      6000 | m      |<br />
| Frank  |      5000 | m      |<br />
| Otto   |      6000 | m      |<br />
| Susan  |      5500 | f      |<br />
| Martin |      5500 | m      |<br />
| Mary   |      5500 | f      |<br />
| Bill   |      5000 | m      |<br />
| June   |      6000 | f      |<br />
+--------+-----------+--------+<br />
9 rows in set (0.01 sec)</code></p>
<p>Poi, eseguiamo le due query. Innanzitutto inizializziamo il contatore.</p>
<p><code><br />
mysql> set @N = 0;<br />
Query OK, 0 rows affected (0.27 sec)<br />
</code></p>
<p>Infine, eseguiamo la query includendo il contatore con una formula per incrementarlo.</p>
<p><code><br />
mysql> SELECT @N := @N +1 AS numero, nome,stipendio,genere FROM persone;<br />
+--------+--------+-----------+--------+<br />
| numero | nome   | stipendio | genere |<br />
+--------+--------+-----------+--------+<br />
|      1 | John   |      5000 | m      |<br />
|      2 | Mario  |      6000 | m      |<br />
|      3 | Frank  |      5000 | m      |<br />
|      4 | Otto   |      6000 | m      |<br />
|      5 | Susan  |      5500 | f      |<br />
|      6 | Martin |      5500 | m      |<br />
|      7 | Mary   |      5500 | f      |<br />
|      8 | Bill   |      5000 | m      |<br />
|      9 | June   |      6000 | f      |<br />
+--------+--------+-----------+--------+<br />
9 rows in set (0.01 sec)<br />
</code></p>
<p>Notate che ogni variabile in una query può essere calcolata una volta sola per ogni record.</p>
<p>Infine, se adottate una soluzione simile, ricordatevi di inizializzare il contatore alla query successiva, altrimenti i vostri record avranno un conteggio in continua crescita!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/lista-record-con-numero-progressivo-54/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Trigger su SELECT</title>
		<link>http://www.stardata.it/trigger-su-select-48/</link>
		<comments>http://www.stardata.it/trigger-su-select-48/#comments</comments>
		<pubDate>Sun, 02 Aug 2009 17:41:26 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[counter]]></category>
		<category><![CDATA[SELECT]]></category>
		<category><![CDATA[trigger]]></category>
		<category><![CDATA[viste]]></category>
		<category><![CDATA[where]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=48</guid>
		<description><![CDATA[I trigger sono aggeggi da usare con cautela. Se abusati, possono portare la vostra applicazione in ginocchio. Tuttavia, sono anche fantastici strumenti per raffinare il controllo in situazioni difficili. Secondo lo standard SQL, i trigger possono essere associati solo alle operazioni di modifica (INSERT, DELETE, UPDATE). E i SELECT, allora? Non sarebbe bello se si [...]]]></description>
			<content:encoded><![CDATA[<p>I trigger sono aggeggi da usare con cautela. Se abusati, possono portare la vostra applicazione in ginocchio. Tuttavia, sono anche fantastici strumenti per raffinare il controllo in situazioni difficili. Secondo lo standard SQL, i trigger possono essere associati solo alle operazioni di modifica (INSERT, DELETE, UPDATE). E i SELECT, allora? Non sarebbe bello se si potesse instaurare un trigger anche per ogni SELECT?</p>
<p>Supponiamo di avere una tabella books e che vogliamo creare una lista personalizzata degli accessi solo per quella tabella. Ecco come si può fare:</p>
<ol>
<li>Create una tabella db_counter che conterrà il numero degli accessi, e una tabella db_log che conterrà i dettagli;</li>
<li>Create una funzione che contenga tutte le operazioni che volete eseguire per ogni SELECT. Questa funzione deve restituire un intero positivo;</li>
<li>Create una vista aggiornabile sulla vostra tabella, aggiungendo una clausola WHERE che usa la funzione appena creata.</li>
</ol>
<p>Andiamo a incominciare:</p>
<p><code><br />
-- ----------------------------------------------------<br />
-- La tabella originale. Quella per cui vogliamo il log<br />
-- La facciamo semplice per non appesantire l'esempio<br />
-- ----------------------------------------------------<br />
CREATE TABLE books (<br />
book_id INT NOT NULL primary key,<br />
title   VARCHAR(40),<br />
pages   INT,<br />
price   DECIMAL(5,2),<br />
author_id INT NOT NULL,<br />
KEY (author_id)<br />
-- ,FOREIGN KEY (author_id) REFERENCES authors (author_id) ON DELETE CASCADE<br />
) ENGINE = InnoDB;</code></p>
<p><code>-- ----------------------------------------------------<br />
-- Il contatore<br />
-- Contiene solo una riga.<br />
-- ----------------------------------------------------<br />
CREATE TABLE db_counter (<br />
id int(11) NOT NULL default '0',<br />
counter bigint(20) default '0',<br />
PRIMARY KEY  (id)<br />
) ENGINE=MyISAM;<br />
</code><br />
<code>-- ----------------------------------------------------<br />
-- L'elenco di dettaglio.<br />
-- Ogni riga contiene informazioni sull'orario ,<br />
-- sull'operazione fatta e l'utente.<br />
-- ----------------------------------------------------<br />
CREATE TABLE db_log (<br />
ts timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,<br />
user varchar(20) NOT NULL,<br />
operation enum('select','insert','update','delete') default 'select',<br />
KEY user_ts (user,ts),<br />
KEY ts (ts)<br />
) ENGINE=MyISAM;<br />
</code><br />
Queste ultime due tabelle contengono le informazioni che ci servono. Prima di continuare, qualche ritocco per essere sicuri che la tabella del contatore possa contenere solo una riga:</p>
<p><code><br />
CREATE TRIGGER counter_insert before INSERT on db_counter<br />
for each row set new.id = 1;<br />
CREATE TRIGGER counter_update before UPDATE on db_counter<br />
for each row set new.id = 1;<br />
</code></p>
<p>E ora alcuni trigger per registrare le operazioni di modifica nella tabella books. (Sarebbe buffo che registriamo gli accessi in lettura e trascuriamo quelli in scrittura!)</p>
<p><code><br />
CREATE TRIGGER book_insert after INSERT on books<br />
for each row set @BOOKCOUNT = get_counter('insert');<br />
CREATE TRIGGER book_delete after DELETE on books<br />
for each row set @BOOKCOUNT = get_counter('delete');<br />
CREATE TRIGGER book_update after UPDATE on books<br />
for each row set @BOOKCOUNT = get_counter('update');<br />
</code></p>
<p>Siamo pronti per creare la funzione:</p>
<p><code><br />
delimiter //<br />
drop function if exists get_counter //<br />
create function get_counter( oper varchar(10) )<br />
returns int<br />
deterministic<br />
begin<br />
if ( ( select count(*) from db_counter ) &gt; 0 ) then<br />
update db_counter set counter = counter + 1;<br />
else<br />
insert into db_counter (counter) values (1);<br />
end if;<br />
insert into db_log (ts, user, operation) values (NULL, user(), oper);<br />
return (select counter from db_counter where id = 1);<br />
end //<br />
delimiter ;<br />
</code></p>
<p>Infine, creiamo la vista aggiornabile per la tabella books.</p>
<p><code><br />
CREATE VIEW mybooks as<br />
SELECT * from books WHERE get_counter() &gt; 0;<br />
</code></p>
<p>Ora, usando mybooks invece di books, ogni accesso a books verrà registrato. Vediamo in azione il nostro meccanismo (notate che usiamo sempre la vista, non la tabella).</p>
<p><code><br />
gmdesk [localhost] {gmax} (test1) &gt; insert into mybooks values (1, 'MySQL', 1000,39.95,1);<br />
Query OK, 1 row affected (0.02 sec)<br />
gmdesk [localhost] {gmax} (test1) &gt; insert into mybooks values (2, 'Programming Perl', 1095,39.95,2);<br />
Query OK, 1 row affected (0.02 sec)<br />
</code></p>
<p><code><br />
gmdesk [localhost] {gmax} (test1) &gt; select * from db_counter;<br />
+----+---------+<br />
| id | counter |<br />
+----+---------+<br />
|  1 |       2 |<br />
+----+---------+<br />
1 row in set (0.00 sec)<br />
</code><br />
<code><br />
gmdesk [localhost] {gmax} (test1) &gt; select * from db_log;<br />
+---------------------+----------------+-----------+<br />
| ts                  | user           | operation |<br />
+---------------------+----------------+-----------+<br />
| 2005-10-24 11:14:22 | gmax@localhost | insert    |<br />
| 2005-10-24 11:14:27 | gmax@localhost | insert    |<br />
+---------------------+----------------+-----------+<br />
2 rows in set (0.00 sec)<br />
</code></p>
<p>Dopo due inserimenti, il contatore è a due, e la tabella dei dettagli ha due record. Proviamo con il nostro obiettivo principale, cioè una SELECT:<br />
<code><br />
gmdesk [localhost] {gmax} (test1) &gt; select count(*) from mybooks;<br />
+----------+<br />
| count(*) |<br />
+----------+<br />
|        2 |<br />
+----------+<br />
1 row in set (0.00 sec)<br />
</code></p>
<p><code>gmdesk [localhost] {gmax} (test1) &gt; select * from db_log;<br />
+---------------------+----------------+-----------+<br />
| ts                  | user           | operation |<br />
+---------------------+----------------+-----------+<br />
| 2005-10-24 11:17:11 | gmax@localhost | insert    |<br />
| 2005-10-24 11:17:23 | gmax@localhost | insert    |<br />
| 2005-10-24 11:18:08 | gmax@localhost | select    |<br />
+---------------------+----------------+-----------+<br />
3 rows in set (0.01 sec)<br />
</code></p>
<p><code><br />
gmdesk [localhost] {gmax} (test1) &gt; select * from db_counter;<br />
+----+---------+<br />
| id | counter |<br />
+----+---------+<br />
|  1 |       3 |<br />
+----+---------+<br />
1 row in set (0.00 sec)<br />
</code></p>
<p>CVD</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/trigger-su-select-48/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dov&#8217;è il NULL?</title>
		<link>http://www.stardata.it/dove-il-null-74/</link>
		<comments>http://www.stardata.it/dove-il-null-74/#comments</comments>
		<pubDate>Tue, 02 Jun 2009 18:53:12 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[count]]></category>
		<category><![CDATA[NULL]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=74</guid>
		<description><![CDATA[Nell&#8217;esaminare dati creati da altri, o dati creati da noi stessi in tempi lontani, spesso accade di volerne ottimizzare la struttura, per migliorare l&#8217;efficienza. Un caso frequente è la ricerca di campi di chiavi esterne con valori NULL, per poter normalizzare le dipendenze da altre tabelle.
Ma comunque i campi NULL possono, quando usati in maniera [...]]]></description>
			<content:encoded><![CDATA[<p>Nell&#8217;esaminare dati creati da altri, o dati creati da noi stessi in tempi lontani, spesso accade di volerne ottimizzare la struttura, per migliorare l&#8217;efficienza. Un caso frequente è la ricerca di campi di chiavi esterne con valori NULL, per poter normalizzare le dipendenze da altre tabelle.</p>
<p>Ma comunque i campi NULL possono, quando usati in maniera irragionevole, influenzare negativamente la precisione o l&#8217;efficienza di una query. Quindi succede spesso che un programmatore si chieda: &#8220;in quali campi di questa tabella c&#8217;è almeno un NULL?&#8221;</p>
<p>Se la tabella è piccola, basta uno sguardo.</p>
<p><code><br />
mysql> select * from persone;<br />
+----+--------+---------+--------+<br />
| id | nome   | cognome | id_dep |<br />
+----+--------+---------+--------+<br />
|  1 | Fabio  | Rossi   |   NULL |<br />
|  2 | Mario  | Bianchi |   NULL |<br />
|  3 | Carlo  | Verdi   |   NULL |<br />
|  4 | Gino   | Neri    |   NULL |<br />
|  5 | Ugo    | Rossi   |      2 |<br />
|  6 | Piero  | Verdi   |      1 |<br />
|  7 | Fulvio | Neri    |   NULL |<br />
|  8 | NULL   | Rossi   |   NULL |<br />
|  9 | NULL   | Bianchi |   NULL |<br />
+----+--------+---------+--------+<br />
9 rows in set (0.02 sec)<br />
</code></p>
<p>Ma se la tabella ha decine di colonne e centinaia di record, il compito di esaminarla a mano è proibitivo. Tuttavia, possiamo agevolarci il compito con qualche query ad hoc. Sappiamo che la funzione COUNT restituisce un conteggio dell&#8217;elemento richiesto. Nella tabella sopra una query</p>
<p></code><br />
mysql> SELECT count(id_dep) FROM persone;<br />
</code></p>
<p>restituisce 2. Questo ci dice che il conteggio salta i valori NULL. Se diamo alla funzione l'elemento '*', però, ci viene restituito un conteggio dei record, a prescindere dal contenuto. Verifichiamo questo fatto.</p>
<p><code><br />
mysql> select * from tuttinull;<br />
+------+------+<br />
| id   | nome |<br />
+------+------+<br />
| NULL | NULL |<br />
| NULL | NULL |<br />
| NULL | NULL |<br />
| NULL | NULL |<br />
+------+------+<br />
4 rows in set (0.01 sec)<br />
</code></p>
<p>Questa tabella ha quattro record, con NULL in tutti i campi.</p>
<p><code><br />
mysql> select count(*) from tuttinull;<br />
+----------+<br />
| count(*) |<br />
+----------+<br />
|        4 |<br />
+----------+<br />
1 row in set (0.01 sec)<br />
</code></p>
<p>Il conteggio, come annunciato, è riferito ai record. Quindi possiamo sfruttare questo fatto per vedere quali campi contengono un NULL, esaminando il conteggio dei singoli campi, e comparandolo al conteggio totale:</p>
<p><code><br />
mysql> select count(*), count(nome), count(cognome), count(id_dep) from persone;<br />
+----------+-------------+----------------+---------------+<br />
| count(*) | count(nome) | count(cognome) | count(id_dep) |<br />
+----------+-------------+----------------+---------------+<br />
|        9 |           7 |              9 |             2 |<br />
+----------+-------------+----------------+---------------+<br />
1 row in set (0.02 sec)<br />
</code></p>
<p>Questo ci dice che ci sono 7 nomi su 9 record, 9 cognomi su 9, e 2 id_dep su 9. Ancora meglio, possiamo chiedere al database di fare il calcolo per noi, e darci direttamente il numero di record mancanti:</p>
<p><code><br />
mysql> select count(*),<br />
    -> count(nome) as nome,<br />
    -> count(*) - count(nome) as `NULL nome`,<br />
    -> count(cognome) as cognome,<br />
    -> count(*) - count(cognome) as `NULL cognome`,<br />
    -> count(id_dep) as id_dep,<br />
    -> count(*) - count(id_dep) as `NULL id_dep`<br />
    -> from persone;<br />
+----------+------+-----------+---------+--------------+--------+-------------+<br />
| count(*) | nome | NULL nome | cognome | NULL cognome | id_dep | NULL id_dep |<br />
+----------+------+-----------+---------+--------------+--------+-------------+<br />
|        9 |    7 |         2 |       9 |            0 |      2 |           7 |<br />
+----------+------+-----------+---------+--------------+--------+-------------+<br />
1 row in set (0.02 sec)<br />
</code></p>
<p>Questo metodo funziona, ma se abbiamo una tabella con tanti campi, non è più un metodo efficiente. È possibile però ottenere lo stesso risultato con un approccio programmatico, come questo esempio in Perl dimostra:</p>
<p><code><br />
#!/usr/bin/perl -w<br />
use strict;<br />
use DBI;<br />
</code><br />
<code><br />
my $dbh = DBI->connect('dbi:mysql:test', 'utente','password')<br />
    or die "problema di connessione\n";<br />
my $colonne = $dbh->selectcol_arrayref(qq{describe persone})<br />
    or die "colonne non trovate";<br />
</code><br />
<code><br />
my $query = qq{select count(*), }<br />
    . join(",", map {qq{count(`$_`) as `$_`,<br />
                   count(*) - count(`$_`) as `NULL $_`} } @$colonne)<br />
    . qq{ from persone};<br />
</code><br />
<code><br />
my $sth = $dbh->prepare($query);<br />
$sth->execute() or die "problema di esecuzione\n";<br />
</code><br />
<code><br />
my $row = $sth->fetchrow_hashref;<br />
printf "%-28s => %s \n", $_, $row->{$_} for @{$sth->{NAME}};<br />
$sth->finish();<br />
</code></p>
<p>Col risultato:</p>
<p><code><br />
count(*)      => 9<br />
id            => 9<br />
NULL id       => 0<br />
nome          => 7<br />
NULL nome     => 2<br />
cognome       => 9<br />
NULL cognome  => 0<br />
id_dep        => 2<br />
NULL id_dep   => 7<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/dove-il-null-74/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Come incrementare un campo con chiave univoca</title>
		<link>http://www.stardata.it/come-incrementare-un-campo-con-chiave-univoca-60/</link>
		<comments>http://www.stardata.it/come-incrementare-un-campo-con-chiave-univoca-60/#comments</comments>
		<pubDate>Tue, 02 Jun 2009 18:12:09 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[ordinamento]]></category>
		<category><![CDATA[rollback]]></category>
		<category><![CDATA[transaction]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=60</guid>
		<description><![CDATA[La domanda viene da uno scorato frequentatore di una mail list: Ho una tabella InnoDB con una chiave univoca sul campo numerico ID. Devo incrementare il campo di cinque unità, ma i miei tentativi non hanno dato frutto. Infatti, se provo a usare la formula SET ID = ID + 5, ottengo un errore di [...]]]></description>
			<content:encoded><![CDATA[<p>La domanda viene da uno scorato frequentatore di una mail list: <em>Ho una tabella InnoDB con una chiave univoca sul campo numerico ID. Devo incrementare il campo di cinque unità, ma i miei tentativi non hanno dato frutto. Infatti, se provo a usare la formula SET ID = ID + 5, ottengo un errore di violazione di chiave. Questa è l&#8217;istruzione che ho tentato</em></p>
<p><code><br />
BEGIN;<br />
UPDATE nometabella SET ID = ID + 1;<br />
COMMIT;<br />
</code></p>
<p>Credevo che InnoDB avrebbe contenuto l&#8217;errore per ogni singola violazione, e aggiornato tutta la tabella, e solo dopo avrebbe considerato che in realtà non c&#8217;è conflitto perché dopo che tutti i record sono stati aggiornati si ritorna a valori univoci. La mia tabella ha 12000 record. Non vorrei essere costretto a usare 12.000 istruzioni di UPDATE. Cosa posso fare?</p>
<p>Questa è stata la mia risposta:</p>
<p><em>InnoDB fa il suo mestiere, fermandosi al primo errore. Il meccanismo transazionale fa sì che la serie di istruzioni venga interrotta al primo errore, dando la possibilità al programmatore di chiamare un ROLLBACK. Quindi, al primo record, ci sarà un conflitto di valori che renderà impossibile l&#8217;aggiornamento. </em></p>
<p>Tuttavia, esiste una soluzione elegante, senza dover inviare 12.000 richieste. Basta chiedere l&#8217;aggiornamento con ordinamento decrescente:</p>
<p><code><br />
UPDATE nometabella SET ID = ID + 1 ORDER BY ID DESC;<br />
</code></p>
<p>In questo modo, il motore aggiornerà la tabella in ordine decrescente di ID, garantendo che non ci saranno conflitti. Se invece si volesse decrementare il valore, basta usare l&#8217;ordinamento crescente (omettendo la clausola DESC).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/come-incrementare-un-campo-con-chiave-univoca-60/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ordinare per condizioni trovate</title>
		<link>http://www.stardata.it/ordinare-per-condizioni-trovate-56/</link>
		<comments>http://www.stardata.it/ordinare-per-condizioni-trovate-56/#comments</comments>
		<pubDate>Tue, 02 Jun 2009 18:03:19 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[efficienza]]></category>
		<category><![CDATA[or]]></category>
		<category><![CDATA[ordinamento]]></category>
		<category><![CDATA[union]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=56</guid>
		<description><![CDATA[FN mi ha proposto un problema interessante. Deve ricercare un archivio usando tre condizioni unite dall&#8217;operatore OR, e gli serve che il risultato sia ordinato in modo che i record che rispondono alla prima condizione vengano per primi, poi quelli della seconda condizione, e infine quelli della terza.
Lui si è arenato alla semplice query senza [...]]]></description>
			<content:encoded><![CDATA[<p>FN mi ha proposto un problema interessante. Deve ricercare un archivio usando tre condizioni unite dall&#8217;operatore OR, e gli serve che il risultato sia ordinato in modo che i record che rispondono alla prima condizione vengano per primi, poi quelli della seconda condizione, e infine quelli della terza.</p>
<p>Lui si è arenato alla semplice query senza ordinamento.</p>
<p><code><br />
mysql> SELECT nome, stipendio, DDN<br />
        FROM impiegati<br />
        WHERE<br />
            nome LIKE 'M%'<br />
            OR DDN < '1960-01-01'<br />
            OR stipendio > 6000;<br />
+---------+-----------+------------+<br />
| nome    | stipendio | DDN        |<br />
+---------+-----------+------------+<br />
| Mario   |      5100 | 1956-10-24 |<br />
| Marco   |      5600 | 1943-03-09 |<br />
| John    |      5550 | 1955-04-02 |<br />
| Maria   |      5700 | 1979-12-11 |<br />
| Colette |      6100 | 1960-08-14 |<br />
| Antonio |      6200 | 1968-12-08 |<br />
| Nina    |      6100 | 1967-05-24 |<br />
+---------+-----------+------------+<br />
7 rows in set (0.00 sec)<br />
</code></p>
<p>Così a prima vista io ho tirato fuori una soluzione che sembra fare al caso suo. Basta ordinare con una funzione CASE che assegna un valore a ogni condizione.</p>
<p><code><br />
mysql> SELECT nome, stipendio, DDN<br />
        FROM impiegati<br />
        WHERE nome like 'M%'<br />
        OR DDN < '1960-01-01'<br />
        OR stipendio > 6000<br />
        ORDER BY<br />
            CASE<br />
                WHEN nome LIKE 'M%' THEN 1<br />
                WHEN DDN < '1960-01-01' THEN 2<br />
                WHEN stipendio > 6000 THEN 3<br />
            END;<br />
+---------+-----------+------------+<br />
| nome    | stipendio | DDN        |<br />
+---------+-----------+------------+<br />
| Mario   |      5100 | 1956-10-24 |<br />
| Marco   |      5600 | 1943-03-09 |<br />
| Maria   |      5700 | 1979-12-11 |<br />
| John    |      5550 | 1955-04-02 |<br />
| Colette |      6100 | 1960-08-14 |<br />
| Antonio |      6200 | 1968-12-08 |<br />
| Nina    |      6100 | 1967-05-24 |<br />
+---------+-----------+------------+<br />
7 rows in set (0.01 sec)<br />
</code></p>
<p>Il brutto di questo approccio, mi fa gentilmente notare FN, è che le condizioni vengono valutate due volte, e anche se questo avviene solo per i record trovati (la clausola ORDER BY viene eseguita dopo che i record sono stati filtrati) è sempre uno spreco e un rallentamento.</p>
<p>Pertanto, concentrandomi sull&#8217;efficienza, ho trovato un&#8217;altra soluzione:</p>
<p><code><br />
mysql> SELECT nome, stipendio, DDN<br />
        FROM impiegati<br />
        WHERE nome like 'M%'<br />
        UNION<br />
        SELECT nome, stipendio, DDN<br />
        FROM impiegati<br />
        WHERE DDN < '1960-01-01'<br />
        UNION<br />
        SELECT nome, stipendio, DDN<br />
        FROM impiegati<br />
        WHERE stipendio > 6000 ;<br />
</code></p>
<p>Questa query risolve il problema dell&#8217;inefficienza dell&#8217;operatore OR con campi diversi, e inoltre risolve anche il problema principale dell&#8217;ordinamento. Benché teoricamente l&#8217;ordine del risultato di una query sia imprevedibile, il modo in cui MySQL organizza i dati in una UNION fa sì che l&#8217;ordinamento sia quello voluto. Si ricordi che una UNION senza la clausola ALL elimina i record duplicati, per cui se un record viene ritrovato grazie alla prima condizione, verrà scartato quando sono valutate la seconda e la terza.<br />
Risultato ottenuto, e ottimizzato allo stesso tempo!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/ordinare-per-condizioni-trovate-56/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Creare un mysqldump con LIMIT</title>
		<link>http://www.stardata.it/creare-un-mysqldump-con-limit-78/</link>
		<comments>http://www.stardata.it/creare-un-mysqldump-con-limit-78/#comments</comments>
		<pubDate>Sat, 02 May 2009 18:58:16 +0000</pubDate>
		<dc:creator>Stardata</dc:creator>
				<category><![CDATA[Tips'n'Tricks]]></category>
		<category><![CDATA[db]]></category>
		<category><![CDATA[LIMIT]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[schema]]></category>

		<guid isPermaLink="false">http://www.stardata.it/?p=78</guid>
		<description><![CDATA[Potrebbe essere necessario a volte creare un campione dei dati di uno o più database, per esempio per poter chiedere assistenza a un consulente o per poter dare assistenza a un utente.
In questi casi, il classico strumento mysqldump non aiuta, perché fra le sue varie utilità non c&#8217;è quella di impostare una clausola LIMIT. È [...]]]></description>
			<content:encoded><![CDATA[<p>Potrebbe essere necessario a volte creare un campione dei dati di uno o più database, per esempio per poter chiedere assistenza a un consulente o per poter dare assistenza a un utente.</p>
<p>In questi casi, il classico strumento <code>mysqldump</code> non aiuta, perché fra le sue varie utilità non c&#8217;è quella di impostare una clausola LIMIT. È vero che si può filtrare con WHERE, ma questo è valido solo se si vogliono estrarre i dati di una tabella alla volta e anche in questo caso la clausola WHERE dovrebbe essere cambiata volta per volta.</p>
<p>Esistono alternative e la più spiccia è quella di creare un programma di estrazione in Perl, per poter prendere con un colpo solo la struttura e N record di tutte le tabelle di tutti i database.</p>
<p>Il semplice script che segue è una semplificazione (ma perfettamente funzionante) di uno strumento analogo che viene usato in StarData per acquisire campioni di dati dagli utenti.</p>
<p>Per limitare la portata dell&#8217;output, qualora non si volesse fornire la lista di tutti i database, è sufficiente eseguire questo script con i privilegi di un utente abilitato solo alla visione di alcuni database.</p>
<p><code><br />
#!/usr/bin/perl -w<br />
use strict;<br />
use DBI;<br />
</code></p>
<p><code><br />
my $host     = 'localhost';<br />
my $user     = 'gmax';       # cambiare<br />
my $password = '';           # cambiare<br />
my $port     = "3306";<br />
my $limit    = 1;<br />
my $db       = "test";<br />
my $DSN      = "DBI:mysql:$db;host=$host;port=$port;"<br />
               . "mysql_read_default_file=$ENV{HOME}/.my.cnf";<br />
</code></p>
<p><code><br />
my $dbh =<br />
  DBI->connect( $DSN, $user, $password, { RaiseError => 1,<br />
    PrintError => 1 } )<br />
  or die "$DBI::errstr\n";<br />
</code></p>
<p><code><br />
for my $db (@{$dbh->selectcol_arrayref("SHOW DATABASES})")<br />
{<br />
    print STDERR "# ------ DATABASE $db\n";<br />
    print "# ------ DATABASE $db\n";<br />
    print "CREATE DATABASE IF NOT EXISTS $db;\n";<br />
    print "USE $db;\n";<br />
    for my $table (@{$dbh->selectcol_arrayref("SHOW TABLES FROM `$db`")})<br />
    {<br />
        print "# TABELLA ($db) $table\n";<br />
        print STDERR "# TABELLA ($db) $table\n";<br />
        my $sth = $dbh->prepare(qq{SHOW CREATE TABLE `$db`.`$table`});<br />
        $sth->execute ;<br />
        my ( $null, $create ) = $sth->fetchrow_array;<br />
        $sth->finish;<br />
        $create =~ s/(?<=CREATE TABLE)/ IF NOT EXISTS /;<br />
        print " $create;\n";<br />
        $sth = $dbh->prepare(<br />
           "SELECT * FROM `$db`.`$table` LIMIT $limit");<br />
        $sth->execute;<br />
        while ( my $rec = $sth->fetchrow_arrayref )<br />
        {<br />
            print qq{INSERT INTO `$db`.`$table` (};<br />
            print join ",", map { "`$_`" } @{ $sth->NAME} };<br />
            print ") VALUES (";<br />
            print join ( ",", map( { $dbh->quote($_) } @$rec ) );<br />
            print ");\n";<br />
        }<br />
    }<br />
}<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.stardata.it/creare-un-mysqldump-con-limit-78/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

