Transact-SQL: creare un password di caratteri casuali

April 21st, 2010 Nicola No comments

Si può fare utilizzando SELECT TOP 1 … ORDER BY NEWID(), da una tabella contenente i caratteri disponibili. Ecco l’esempio:

WITH char_table AS (
	SELECT 'a' AS a
    UNION
	SELECT 'b' AS a
        ...
        /* inserire qui l'elenco dei caratteri che si 
            vogliono prendere in considerazione */
        ...
	UNION
	SELECT 'z' AS a
)
SELECT
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid()) +
(SELECT top 1 a FROM char_table ORDER BY newid())
Categories: Database, SQL Server Tags: , ,

SQL Server: Hash and Stream aggregate algorithm

April 19th, 2010 Nicola No comments

SQL Server utilizza due operatori fisici per gestire le aggregazioni (GROUP BY): lo Stream aggregate e l’Hash aggregate. Alla base di questi due operatori vi sono due diversi algoritmi, con caratteristiche diverse, che SQL Server sceglie a seconda della situazione.

Un esempio:

DROP TABLE ORDINI
CREATE TABLE ORDINI (IDPRODOTTO INT, QUANTITA INT)
GO
 
INSERT INTO ORDINI VALUES (1, 1)
GO 50
INSERT INTO ORDINI VALUES (2, 1)
GO 30
INSERT INTO ORDINI VALUES (3, 1)
GO 60

La query di aggregazione:

SELECT IDPRODOTTO, COUNT(IDPRODOTTO) AS [ORDINATO]
  FROM ORDINI
 GROUP BY IDPRODOTTO

Viene risolta in questo modo attraverso l’operatore Stream:

aggregate1

Ora inseriamo un pò di record e rifacciamo la stessa query:

INSERT INTO ORDINI VALUES (1, 1)
GO 5000
INSERT INTO ORDINI VALUES (2, 1)
GO 3000
INSERT INTO ORDINI VALUES (3, 1)
GO 6000
 
SELECT IDPRODOTTO, COUNT(IDPRODOTTO) AS [ORDINATO]
  FROM ORDINI
 GROUP BY IDPRODOTTO

Il piano di esecuzione questa volta prevere l’operatore Hash Match, scelta dovuta alla dimensione maggiore della tabella:

aggregate2

I due algoritmi alla base degli operatori sono:

  • Algoritmo hash
    Per ogni riga in input
    {
       Calcola il valore hash basato sulle colonne incluse nella clausola group by
       cerca il valore trovato nella tabella hash
       if non trovato
          inserisce una nuova riga nella tabella hash</code>
       else
          aggiorna la riga trovato con il nuovo valore</code>
       }
       restituisce tutte le righe presenti nella tabella hash
  • Algoritmo Stream

    pulisce i totali correnti
    pulisce il valore attuale delle colonne di group by
    per ogni riga in input
    {
       se la riga attuale non corrisponde con il valore attuale delle colonne del group by
       {
          restituisce i totali corrente
          pulisce i totali correnti
          imposta il valore attuale delle colonne di group by con la riga corrente
       }
       update del totale di aggregazione
    }

SQL: Ottenere solo un record con determinate caratteristiche da ogni raggruppamento di un group by

April 15th, 2010 Nicola No comments

Oggi ho guadagnato una medaglia per aver risolto questo problema:

Data questa tabella:

columnn1 	Column2
a 		2010-04-15 00:00:00.000
a 		2010-04-17 00:00:00.000
b 		2010-04-17 00:00:00.000
b 		2010-04-15 00:00:00.000
b 		2010-04-20 00:00:00.000
c 		2010-04-19 00:00:00.000
d 		2010-04-12 00:00:00.000
d 		2010-04-13 00:00:00.000

come si fa ad estrarre, per ogni codice, quello la cui data è più vicina alla data corrente? Ovvero ad avere il risultato:

a 		2010-04-15 00:00:00.000
b 		2010-04-15 00:00:00.000
c 		2010-04-19 00:00:00.000
d 		2010-04-13 00:00:00.000

La soluzione è:

  SELECT column1, column2
    FROM prova
   WHERE
    (SELECT count(*)
       FROM prova AS p
      WHERE p.column1 = prova.column1
        AND  abs(datediff(day, p.column2, getdate())) <  abs(datediff(day, prova.column2, getdate()))
    ) = 0

Il problema è simile al solito dilemma: dato un group by, come faccio ad estrarre solo i record che hanno valore massimo (o che hanno massima occorrenza o altre condizioni)?
Il risultato è un self-join che in maniera generica può essere scritto così:


select [campi]

from [tabella] t1

where

( select [funzione scalare per la scelta dei campi]

from [tabella] as t2

where t1.key = t2.key

and [condizione di confronto fra due campi (anche calcolati) della tabella]

) [condizione sulla funzione scalare definita]

Il classico esempio: di ogni gruppo vogliamo solo il record di valore massimo

select key, valore

from [tabella] t1

where

( select count(*)

from [tabella] as t2

where t1.key = t2.key

and t2.valore > t1.valore -- prendo il maggiore

) = 0

Ovvero, prendo il record della prima tabella che non ha record con valore superiore sulla seconda tabella.
Se volessimo prendere il valore minino basterebbe invertire la condizione di confronto fra i due valori:


select key, valore

from [tabella] t1

where

( select count(*)

from [tabella] as t2

where t1.key = t2.key

and t2.valore < t1.valore -- prendo il minore

) = 0

Se vogliamo prendere i 2 valori più alti di ogni gruppo, basta cambiare la condizione finale:


select key, valore

from [tabella] t1

where

( select count(*)

from [tabella] as t2

where t1.key = t2.key

and t2.valore minore t1.valore

) < 2 -- numero di record restituiti per la condizione

Categories: Database Tags: ,

Transact-SQL: Copia di dati fra tabelle: differenza fra DROP TABLE + SELECT INTO e DELETE + INSERT

April 6th, 2010 Nicola No comments

Date due tabelle TABELLA_SORGENTE e TABELLA_DESTINAZIONE, per copiare tabella sorgente su tabella destinazione ci sono anche queste due soluzioni:

  • Utilizzo dell’accoppiata DROP TABLE + SELECT INTO: assolutamente da EVITARE se avete a cuore la vostra tabella di destinazione.
  • Utilizzo dell’accoppiata DELETE + INSERT: sicuramente il metodo da usare.

Il motivi per non usare l’accoppiata DROP TABLE + SELECT INTO sta nel fatto che tutti i CONSTRAINT ed i vincoli strutturale presenti sulla tabella che viene eliminata non vengono ovviamente ricreati dalla SELECT INTO ma vengono persi. Quella che viene creata è una tabella ad-hoc per i dati che deve contenere, nulla di più.

Un esempio concreto:

Creiamo una tabella sorgente e mettiamoci dei dati dentro:

CREATE TABLE TABELLA_SORGENTE (ID INT, DESCRIZIONE VARCHAR(20) NOT NULL DEFAULT 'PIPPO')

INSERT INTO TABELLA_SORGENTE VALUES (1,'PAPERINO');
INSERT INTO TABELLA_SORGENTE VALUES (2,'PLUTO');

Creiamo la tabella di destinazione, speculare alla tabella sorgente:

CREATE TABLE TABELLA_DESTINAZIONE (ID INT, DESCRIZIONE VARCHAR(20) NOT NULL DEFAULT 'PIPPO')

Utilizzando DELETE + INSERT abbiamo:

DELETE FROM TABELLA_DESTINAZIONE
INSERT INTO TABELLA_DESTINAZIONE SELECT * FROM TABELLA_SORGENTE

A questo punto la query

INSERT INTO TABELLA_DESTINAZIONE (ID) VALUES (3)

va a buon fine, e la tabella si presenta con questi record

1 PAPERINO
2 PLUTO
3 PIPPO

Utilizzando invece DROP TABLE + SELECT INTO abbiamo:

DROP TABLE TABELLA_DESTINAZIONE
SELECT * INTO TABELLA_DESTINAZIONE FROM TABELLA_SORGENTE

Sembrerebbe tutto uguale, ma il risultato della query

INSERT INTO TABELLA_DESTINAZIONE (ID) VALUES (3)

è il seguente:

Cannot insert the value NULL into column 'DESCRIZIONE', table 'NICOLA.dbo.TABELLA_DESTINAZIONE'; column does not allow nulls. INSERT fails.
The statement has been terminated.

Questo perchè la condizione DEFAULT ‘PIPPO’ della dichiarazione iniziale della tabella

CREATE TABLE TABELLA_DESTINAZIONE (ID INT, DESCRIZIONE VARCHAR(20) NOT NULL DEFAULT 'PIPPO')

ora non è più presente.
Quindi non fate mai DROP TABLE + SELECT INTO su tabelle cui volete bene.

c#: PerformanceCounterCategory e PerformanceCounter per monitorare lo stato della memoria di un processo all’interno del CLR .NET

March 9th, 2010 Nicola No comments

Con queste classi è possibile monitorare molti aspetti vitali delle nostre applicazioni, come utilizzo di memoria, i/o sul disco ecc..
Questo link della Microsoft contiene molta documentazione interessante.

Il codice che segue analizza lo stato della memoria relativa un singolo processo,  corrispondente ad una applicazione .NET.

// Questa è l'area di cui vogliamo i contatori, ovvero ".NET CLR Memory"
System.Diagnostics.PerformanceCounterCategory area = 
   new System.Diagnostics.PerformanceCounterCategory(".NET CLR Memory");

Il seguente codice mostra a video tutte le istanze dentro l’area .NET CLR Memory analizzabili in quel momento sulla macchina:

string[] instanceNames;
 
instanceNames = mycat.GetInstanceNames();
for (int i=0; i < instanceNames.Length; i++)
{
   Console.WriteLine("Nome Istanza: {0}", instanceNames[i]);
}

Il suo output è simile a questo:

C:\Documents and Settings\colonnan\Documenti\Visual Studio 2008\Projects\Console
Application3\ConsoleApplication3\bin\Debug>MonitorProcesso.exe
Nome Istanza: <strong>MonitorProcesso</strong>
Nome Istanza: devenv
Nome Istanza: _Global_
Nome Istanza: MonitorProcesso.vshost

In questo esempio "MonitorProcesso.exe" è l’applicazione che sto lanciando, rappresentato dall’istanza "MonitorProcesso".
Per analizzare lo stato di memoria di MonitorProcesso.exe:

//Array dei contatori di un singolo processo.
System.Diagnostics.PerformanceCounter[] counters = 
   area.GetCounters("MonitorProcesso");
 
for (int i = 0; i < counters.Length;i++)
{
   Console.WriteLine("Performance counter: {0} = {1}", 
   counters[i].CounterName, counters[i].NextValue());
}

Il cui risultato è:

Performance counter: # Gen 0 Collections = 1
Performance counter: # Gen 1 Collections = 0
Performance counter: # Gen 2 Collections = 0
Performance counter: Promoted Memory from Gen 0 = 271400
Performance counter: Promoted Memory from Gen 1 = 0
Performance counter: Gen 0 Promoted Bytes/Sec = 0
Performance counter: Gen 1 Promoted Bytes/Sec = 0
Performance counter: Promoted Finalization-Memory from Gen 0 = 0
Performance counter: Promoted Finalization-Memory from Gen 1 = 1320
Performance counter: Gen 0 heap size = 524288
Performance counter: Gen 1 heap size = 524480
Performance counter: Gen 2 heap size = 12
Performance counter: Large Object Heap size = 541560
Performance counter: Finalization Survivors = 0
Performance counter: # GC Handles = 35
Performance counter: Allocated Bytes/sec = 0
Performance counter: # Induced GC = 0
Performance counter: % Time in GC = 1,227221
Performance counter: Not Displayed = 0
Performance counter: # Bytes in all Heaps = 1131064
Performance counter: # Total committed Bytes = 1662976
Performance counter: # Total reserved Bytes = 3,354624E+07
Performance counter: # of Pinned Objects = 0
Performance counter: # of Sink Blocks in use = 0