[mysql] Creare tabella pivot dinamica in MySQL (per esordienti totali)

Con il MySQL è possibile generare tabelle pivot dinamiche, ovvero che contengano nomi di colonne automatiche, in base ad una tabella di riferimento.

Quello che vogliamo ottenere sarà un risultato come il seguente:

Prima di cominciare creiamo un database con un po’ di dati in modo da poterci lavorare.

1. Creazione dati di partenza

Creiamo 3 tabelle: prodotti, agenti e vendite. Di seguito riporto le query per creare le singole tabelle ed inserire i dati di esempio.

PRODOTTI

Per creare la tabella prodotti eseguiamo la seguente query.

Inseriamo i dati nel modo seguente:

AGENTI

Andiamo adesso a creare la tabella degli agenti, in modo analogo:

Ed inseriamo i dati:

VENDITE

Per le vendite andremo a creare la tabella in questo modo:

A differenza delle precedenti due, qui voglio inserire dei dati casuali. Per inserire una riga di dati casuali, pescati dalle precedenti due tabelle, potremmo scrivere:

Faccio notare che con l’istruzione ROUND(1+RAND()*4,0) inseriamo un valore intero casuale tra 1 e 5. Se volessimo generalizzare il procedimento potremmo scrivere ROUND(n+RAND()*(m-n),0) dove n è il minimo, mentre m è il massimo.

Con n = 3 e m = 8 avremmo infatti le seguenti operazioni:

  1. RAND() genera un valore casuale decimale da 0 a 1
  2. Moltiplicando per (8-3) il valore casuale sarà tra 0 e 5.
  3. Sommano il risultato a 3 il valore casuale sarà un decimale tra 3 e 8. Potrebbe essere per esempio 6,4.
  4. Usando ROUND() arrotondiamo a 0 cifre decimali ottenendo quindi un intero.

Se volessimo inserire più di una riga in una singola operazione, possiamo farlo scrivendo una procedura.

Dichiariamo quindi una procedura nel modo seguente, che inserisca 1000 valore casuali per volta.

Chiamiamo la procedura digitando:

2. Creazione tabella pivot

Per creare una tabella pivot manualmente utilizziamo CASE WHEN ... END. A mano potremmo scrivere una query come quella di seguito:

Ogni colonna della pivot sarà generata da:

In questo caso noi abbiamo una tabella dei prodotti, dove sappiamo esserci all’id = 1 il prodotto chiamato mele.

Con SUM() sommiamo i valori (avremmo potuto usare COUNT(), AVG(), MAX(), MIN() ecc.) e con ROUND() arrotondiamo. Quest’ultima funzione in particolare non sarebbe necessaria, ma la utilizzo solo perché, visto che si parla di importi in euro, non ha senso calcolare un risultato al di sotto dei centesimi.

Quello che vogliamo fare ora è poter scrivere, quel pezzo di query, in modo iterativo, ripetendola per CIASCUN valore della tabella prodotti.

In particolare potremmo eseguire una query come la seguente:

Il risultato di questa query sarebbe un singolo campo contenente i seguenti valori:

Faccio notare che grazie a GROUP_CONCAT() vengono inserite le virgole tra le singole righe.

Andiamo a creare quindi una procedura come la seguente:

Eseguiamo, come prima, la procedura:

Otterremo il risultato cercato.

Dobbiamo usare una procedura per evitare di incorrere in una serie di incongruenze legate all’interrogazione del database.

DIfatti, usando il PHPMyAdmin e lanciando solamente l’istruzione:

Si incorrere nel seguente errore:

Fatal error: Uncaught Error: Call to a member function getClauses() on null in D:\xampp\phpMyAdmin\vendor\phpmyadmin\sql-parser\src\Utils\Query.php:564 Stack trace: #0 D:\xampp\phpMyAdmin\vendor\phpmyadmin\sql-parser\src\Utils\Query.php(681): PhpMyAdmin\SqlParser\Utils\Query::getClause(NULL, NULL, 'ORDER BY', -1, false) #1 D:\xampp\phpMyAdmin\libraries\DisplayResults.php(1385): PhpMyAdmin\SqlParser\Utils\Query::replaceClause(NULL, NULL, 'ORDER BY', '') #2 D:\xampp\phpMyAdmin\libraries\DisplayResults.php(4376): PMA\libraries\DisplayResults->_getUnsortedSqlAndSortByKeyDropDown(Array, '') #3 D:\xampp\phpMyAdmin\libraries\sql.lib.php(1689): PMA\libraries\DisplayResults->getTable(Object(mysqli_result), Array, Array, false) #4 D:\xampp\phpMyAdmin\libraries\sql.lib.php(1980): PMA_getHtmlForSqlQueryResultsTable(Object(PMA\libraries\DisplayResults), './themes/pmahom...', NULL, Array, false, 6, 6, NULL, Object(mysqli_result), Array) #5 D:\xampp\phpMyAdmin\libraries\sql.lib.php(2199): PMA_getQueryResponseForResultsReturned(Object(my in D:\xampp\phpMyAdmin\vendor\phpmyadmin\sql-parser\src\Utils\Query.php on line 564

3. Utilizzo della tabella nel PHP

Leggiamo la tabella tramite il PHP. Per farlo utilizzerò la classe MySQLdb come in questo esercizio sul MySQL Cluster.

Creiamo nella root del nostro sito un file MySQLdb.php contenente il seguente codice:

Creiamo adesso un file index.php, nella stessa posizione, contenente il seguente codice:

Vediamo in particolare come nel vettore $prodotti abbiamo l’elenco di tutti i prodotti e quindi delle colonne della pivot, che si trovano in $dati. Il risultato che otterremo sarà simile a questo:

 

Vedi articolo

[magento] Errore SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘0009032’ for key ‘UNQ_SALES_FLAT_ORDER_INCREMENT_ID’

Soluzione1: Questo errore si presenta quando si effettua un ordine su Magento, si viene indirizzati al pagamento, ma il pagamento non va a buon fine e si viene rimandati indietro sul negozio. Per risolvere questo problema è sufficiente modificare il file app/code/core/Mage/Sales/Model/Resource/Quote.php come indicato di seguito

Per risolvere il problema posizioniamoci nella cartella principale del negozio in Magento. Dopodiché andiamo a modificare il file app/code/core/Mage/Sales/Model/Resource/Quote.php

Apriamolo e cerchiamo la seguente parte di codice

Alla linea quattro modifichiamo

In modo che diventi:

Salviamo il tutto.

Soluzione2: Questo errore si presenta quando per qualche ragione sono stati inseriti ordini sopra l’ultimo incrementale registrato in eav_entity_type. Per risolvere il problema è sufficiente incrementare sopra l’ultimo valore registrato il valore dell’incrementale. 

Di seguito discutiamo il problema e la soluzione nel dettaglio.

Anzitutto l’errore che si presenta è simile a questo:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '0009032' for key 'UNQ_SALES_FLAT_ORDER_INCREMENT_ID', query was: INSERT INTO sales_flat_order (coupon_code, protect_code, shipping_description, is_virtual, store_id, customer_id, base_discount_amount, base_grand_total, base_shipping_amount, base_shipping_tax_amount, base_subtotal, base_tax_amount, base_to_global_rate, base_to_order_rate, discount_amount, grand_total, shipping_amount, shipping_tax_amount, store_to_base_rate, store_to_order_rate, subtotal, tax_amount, total_qty_ordered, customer_is_guest, customer_note_notify, customer_group_id, quote_id, base_shipping_discount_amount, base_subtotal_incl_tax, shipping_discount_amount, subtotal_incl_tax, weight, customer_dob, increment_id, applied_rule_ids, base_currency_code, customer_email, customer_firstname, customer_lastname, customer_middlename, customer_prefix, customer_suffix, customer_taxvat, discount_description, global_currency_code, order_currency_code, remote_ip, shipping_method, store_currency_code, store_name, x_forwarded_for, customer_note, created_at, updated_at, total_item_count, customer_gender, hidden_tax_amount, base_hidden_tax_amount, shipping_hidden_tax_amount, base_shipping_hidden_tax_amnt, shipping_incl_tax, base_shipping_incl_tax, gift_message_id, payment_fee_amount, base_payment_fee_amount, payment_installment_fee_amount, base_payment_installment_fee_amount, payment_tax_amount, base_payment_tax_amount, referral_code, ebizmarts_abandonedcart_flag, payment_fee_tax, base_payment_fee_tax, payment_percentage_fee, base_payment_percentage_fee) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '1975-10-01 00:00:00', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '2018-11-28 11:14:46', '2018-11-28 11:14:46', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

Questo errore si presenta perché si sta cercando di inserire una chiave duplicata in sales_flat_order. Prima di tutto verifichiamo la cosa spostandoci in sales_flat_order ed effettuando la seguente query:

Nel mio caso il risultato della query era 10.009. Adesso spostiamoci su eav_entity_store. Qui dovremmo vedere una tabella con tutti gli incrementali. Cerchiamo quello che ci interessa. Per trovarlo guardiamo i campi entity_type_id e store_id. Gli entity_type_id hanno come riferimento la tabella eav_entity_type.

Per esempio il codice 5 corrisponde agli ordini, mentre il codice 6 alle fatture/ordini.

Nel mio caso mi accorgo che increment_last_id è su 9033, mentre il massimo valore inserito in sales_flat_order è di 10.009. Quindi mi è sufficiente portare questo valore al di sopra di quello attuale, per esempio impostandolo su 10.010.

Una volta fatto il problema è risolto.

Vedi articolo

[vba] Classe per interrogare in SQL un foglio excel

Obiettivo: creare una classe per poter interrogare tramite query SQL un foglio excel, leggendo e scrivendo i dati come se lavorassimo con tabelle SQL

Questo esercizio riprende il punto direttamente da questo precedente: Interrogare foglio di Excel con SQL in VBA

Dopo aver visto come poter interrogare con query SQL un foglio excel, vediamo come creare una classe ad hoc che ci permetta di leggere e scrivere i dati mediante query SQL. Per farlo anzitutto aggiungiamo una nuova classe:

Premendo ALT+F11 apriamo l’editor Visual Basic, ci posizioniamo su un qualunque punto del progetto a sinistra e clicchiamo col destro (per esempio su Form), poi scegliamo Inserisci ⇒ Modulo classe. A questo punto verrà creata una nuova classe. Per rinominarla è sufficiente selezionarla e poi spostarsi nel riquadro delle proprietà.

A questo punto incolliamo dentro la classe appena creata (tenendo presente poi il nome scelto) il seguente codice:

Faccio notare come nel metodo dbout dobbiamo fare un’operazione un po’ superflua in apparenza, ovvero contare prima le righe e le colonne, per poi ridimensionare il vettore result. Questa operazione è necessaria perché ReDim, in VBA, non permette di ridimensionare l’intera matrice (vettore di vettori), ma solo l’ultima dimensione. Quindi, per esempio, result(4,10) potrebbe diventare result(4,20), ma non result(5,10) oppure result(5,20). Un’alternativa sarebbe quella di creare una funzione che trasponga il vettore e lo modifichi, ritrasponendolo di nuovo, ma dal punto di vista del calcolo credo sarebbe molto più oneroso che fare così.

Fatto questo potremo utilizzare la nostra nuova classe nella maniera seguente.

Una volta creata la classe può essere esportata, oppure importata nel modo seguente:

Per chi volesse è possibile scaricare la classe già pronta cliccando qui: DbSQL.zip

Hash MD5: A2A18A42BB11AEA86DF1AB4F547B4414

La classe è stata testata su Excel 2016.

Vedi articolo