19 May 2009

Numerare le righe con SQL Server 2005

Questo metodo è ancora più rapido di quello precedente (vedi sotto), perché prescinde dall'ordinamento dei campi (il trucco è usare la clausola ORDER BY GETDATE()

DECLARE @tab1 AS TABLE
(
    id int PRIMARY KEY
  , name nvarchar(20)
)


INSERT @tab1 (id, name)
      SELECT 100, 'Marty McFly'
UNION SELECT 200, 'Emmett Brown'
UNION SELECT 300, 'Biff Tannen'


SELECT *
FROM @tab1


SELECT   *
  , row_id = ROW_NUMBER() OVER (ORDER BY GETDATE())
FROM    @tab1


SELECT   *
  , row_id = ROW_NUMBER() OVER (ORDER BY GETDATE())
FROM    @tab1
WHERE id >= 200

Output:
id          name
----------- --------------------
100         Marty McFly
200         Emmett Brown
300         Biff Tannen


id          name                 row_id
----------- -------------------- --------------------
100         Marty McFly          1
200         Emmett Brown         2
300         Biff Tannen          3


id          name                 row_id
----------- -------------------- --------------------
200         Emmett Brown         1
300         Biff Tannen          2


Sintassi di ROW_NUMBER()

Trovato qui.



Illustriamo come è possibile eseguire la stessa operazione in SQL Server 2005 sfruttando la nuova funzione ROW_NUMBER().
La funzione ROW_NUMBER() restituisce il numero sequenziale di una riga in una partizione di un result set, partendo da 1 per la prima riga di ogni partizione. La clausola OVER(), obbligatoria, determina la partizione e l'ordinamento del rowset prima che la funzione associata venga applicata.
Vediamo un paio di esempi:
USE tempdb;
GO

/* Definisco la tabella dbo.myTable */
CREATE TABLE dbo.myTable(
Codice varchar(6) NOT NULL,
Descrizione varchar(25) NOT NULL
);
GO

/* La popolo */
SET NOCOUNT ON;
INSERT dbo.myTable VALUES('SQL2K', 'SQL Server 2000');
INSERT dbo.myTable VALUES('SQL2K5', 'SQL Server 2005');
INSERT dbo.myTable VALUES('VB6', 'Visual Basic 6');
INSERT dbo.myTable VALUES('VB2K5', 'Visual Basic 2005');
INSERT dbo.myTable VALUES('VB2K5E', 'Visual Basic 2005 Express');
SET NOCOUNT OFF;
GO

/* Query */
SELECT
Codice
, Descrizione
, ROW_NUMBER() OVER(ORDER BY Codice) AS 'Row Number'
FROM dbo.myTable;
GO

/* Output:

Codice Descrizione Row Number
------ ------------------------- --------------------
SQL2K SQL Server 2000 1
SQL2K5 SQL Server 2005 2
VB2K5 Visual Basic 2005 3
VB2K5E Visual Basic 2005 Express 4
VB6 Visual Basic 6 5

(5 row(s) affected)

*/

/* Pulizia */
DROP TABLE dbo.myTable;
Questa volta definisco una partizione sulla colonna Codice:
/* Definisco la tabella dbo.myTable */
CREATE TABLE dbo.myTable(
Codice varchar(6) NOT NULL,
Descrizione varchar(25) NOT NULL
);
GO

/* La popolo */
SET NOCOUNT ON;
INSERT dbo.myTable VALUES('SQL', 'SQL Server 2000');
INSERT dbo.myTable VALUES('SQL', 'SQL Server 2005');
INSERT dbo.myTable VALUES('VB', 'Visual Basic 6');
INSERT dbo.myTable VALUES('VB', 'Visual Basic 2005');
INSERT dbo.myTable VALUES('VB', 'Visual Basic 2005 Express');
SET NOCOUNT OFF;
GO

/* Query */
SELECT
Codice
, Descrizione
, ROW_NUMBER() OVER(PARTITION BY Codice ORDER BY Codice) AS 'Row Number'
FROM dbo.myTable;
GO

/* Output:

Codice Descrizione Row Number
------ ------------------------- --------------------
SQL SQL Server 2000 1
SQL SQL Server 2005 2
VB Visual Basic 6 1
VB Visual Basic 2005 2
VB Visual Basic 2005 Express 3

(5 row(s) affected)

*/

/* Pulizia */
DROP TABLE dbo.myTable;

Ridurre le dimensioni del Transaction Log in SQL Server 2000

from: http://www.dotnethell.it/tips/ShrinkTLOGSql2000.aspx
Il Transaction Log registra tutte le modifiche effettuate in un database ed una serie di informazioni aggiuntive che gli permettono di annullare (roll back) o ripristinare (roll forward) tali modifiche in caso di un errore di sistema (Hardware o Software) oppure a causa di un comando esplicito di ROLLBACK della transazione garantendo in questo modo la consistenza dei dati. Dal punto di vista fisico il Transaction Log è costituito da uno o più files associati ad un database durante la sua creazione/modifica.
Vediamo cosa succede quando effettuiamo una modifica ad una tabella.
SQL Server memorizza le modifiche nel Transaction Log su disco nel momento in cui tali modifiche sono eseguite, prima che vengano memorizzate nel database.
Il processo può essere brevemente riassunto nei seguenti punti:
Una applicazione invia una modifica a SQL Server; Non appena la modifica viene eseguita, le data pages coinvolte vengono lette da disco ed inserite nel buffer cache (nel caso non siano già presenti a causa di un precedente comando); Ogni istruzione di modifica viene registrata nel log non appena viene eseguita. Le modifiche sono sempre registrate nel log e scritte su disco prima che vengano inserite nel database.
Questo tipo di log viene chiamato write-ahead;
Ciclicamente si verifica il processo di checkpoint che scrive fisicamente nel database tutte le transazioni completate.
Se si verifica un problema (schianta un disco, va via la corrente e come al solito l'elettricista si è dimenticato di mettere sotto UPS i server, prende fuoco la sala fredda... adesso sto esagerando ;-)) all'accensione successiva parte automaticamente un processo di recovery che utilizza il transaction log per effettuare il roll forward di tutte le transazioni concluse correttamente (committed) e l'annullamento (roll back) di quelle non ancora concluse.
I transaction markers nel log sono utilizzati durante il processo automatico di recovery per determinare i punti di partenza e di fine di una transazione. Una transazione è considerata completata quanto il marker BEGIN TRANSACTION ha il corrispondente marker COMMIT TRANSACTION. Le data pages sono scritte su disco quando si verifica il processo di checkpoint.
Per rimuovere dal transaction log le transazioni scritte nel database durante il checkpoint, puoi utilizzare il comando T-SQL BACKUP LOG. Se non ti interessa creare un file di backup, puoi specificare le opzioni WITH NO_LOG oppure TRUNCATE_ONLY (sono sinonimi).
Questa operazione non rilascia lo spazio allocato dal file(s) .ldf al file system. Per liberare spazio effettua uno shrink del file di log mediante il comando DBCC SHRINKFILE ().
Inoltre ti suggerisco di valutare quale recovery model è più adatto alle tue esigenze.
In SQL Server 2000 puoi selezionare per ogni database 3 recovery model in modo da determinare la modalità di backup dei dati e di stabilire l'esposizione al rischo di perdita di dati:
Simple Recovery
Con il Simple Recovery Model, il database può essere recuperato in base ai dati presenti nell'ultimo backup effettuato. Comunque con questa impostazione non è possibile ripristinare il database al momento del crash o ad un determinato periodo temporale, in quanto le transazioni vengono eliminate dal Transaction Log ad ogni checkpoint. Per fare questo occorre selezionare il Full Recovery o Bulk-Logged Recovery Model.
Full Recovery
Il Full Recovery Model utilizza i database backup ed i transaction log backup per fornire una protezione completa sulla perdita di dati. Se uno o più data file viene danneggiato, è possibile recuperare tutte le transazioni concluse con successo. Le transazioni in corso durante il crash ma non ancora concluse, saranno annullate. Il Full Recovery permette di ripristinare il database al momento del crash o ad uno specifico periodo temporale. Per garatire questo grado di recovery, tutte le operazioni, incluse quelle bulk come la SELECT INTO, CREATE INDEX e operazioni di bulk loading sono loggate completamente.
Bulk-Logged Recovery
Il Bulk-Logged Recovery Model fornisce protezione dei dati garantendo elevate prestazioni ed una bassa occupazione del transaction log durante le operazioni di bulk copy.
Le seguenti operazioni sono minimamente loggate:
- SELECT INTO
- Operazioni di Bulk load (bcp e BULK INSERT)
- CREATE INDEX (anche sulle indexed views)
- Operazioni sui campi text e image (WRITETEXT e UPDATETEXT)
Con il Bulk-Logged Recovery Model l'esposizione alla perdita di dati durante queste operazioni di bulk copy è maggiore rispetto al Full Recovery Model. Mentre le operazioni bulk copy sono loggate completamente nel caso di Full Recovery Model, lo sono minimamente e non possono essere controllare a livello di singola operazione impostando il Bulk-Logged Recovery Model.
In caso di guasto, un data file danneggiato può comportare un intervento manuale per ripristinare l'integrità dei dati.
Per ulteriori informazioni consulta sui Books Online i seguenti paragrafi:
- http://msdn.microsoft.com/library/en-us/architec/8_ar_da2_7vaf.asp
- http://msdn.microsoft.com/library/en-us/createdb/cm_8_des_03_6ohf.asp
- http://msdn.microsoft.com/library/en-us/tsqlref/ts_dbcc_8b51.asp

SQL Try/Catch

This stored procedure formats and re-throws the error:
/****** Oggetto:  StoredProcedure [dbo].[sys_RethrowError_sp]    Data script: 03/12/2009 15:41:53 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[sys_RethrowError_sp] AS
	
-- SE NON CI SONO ERRORI, ESCE
IF ERROR_NUMBER() IS NULL RETURN


-- VARIABILI PER MEMORIZZARE L'ERRORE
DECLARE @ErrorMessage NVARCHAR(4000)
DECLARE @ErrorSeverity INT
DECLARE @ErrorLine INT
DECLARE @ErrorProcedure NVARCHAR(200)


-- IMPOSTA IL MESSAGGIO DI ERRORE
SET	@ErrorSeverity = ERROR_SEVERITY()
SET	@ErrorLine = ERROR_LINE()
SET	@ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-')


SET @ErrorMessage = N'Procedura: %s, Linea: %d, Messaggio di errore: ' + CHAR(10) + ERROR_MESSAGE()


-- RILANCIA L'ERRORE
RAISERROR 
(
	@ErrorMessage, 
	@ErrorSeverity, 
	1,               
	@ErrorProcedure, -- parameter: original error procedure name.
	@ErrorLine       -- parameter: original error line number.
)


GO
And here's a an example using the previous stored procedure:
/****** Oggetto:  StoredProcedure [dbo].[x_test_trycatch_sp]    Data script: 03/12/2009 15:41:54 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE procedure [dbo].[x_test_trycatch_sp]
as

set nocount on

create table #tab
(
	Nome nvarchar(10) not null primary key,
	Valore int null
)


begin try
	begin transaction
		insert #tab(nome, valore) values ('a', 1)
		insert #tab(nome, valore) values ('b', 2)
		insert #tab(nome, valore) values ('c', 3)
		-- causo l'errore
		insert #tab(nome, valore) values ('b', 2)
	commit
end try

begin catch
	rollback
	exec sys_RethrowError_sp
end catch


select * from #tab

GUID Visualizer Broken in VB.NET and How to Fix It

A colleague brought over a strange problem he was seeing while debugging a VB.NET project. During a debugging session he hovered his mouse over a variable of type System.Guid. You normally expect the standard visualizer for the type to appear, but it was displaying "Empty". Unfortunately, this led to some thrashing and gnashing of teeth because this 'bug' caused him to incorrectly suspect a problem with his code.

After a bit of searching and reflecting over the System.Guid class, I found that it is the visualizer for GUIDs in VB.NET that is 'broken'. The 'bug' exists in both Visual Studio 2005 and Visual Studio 2008. Dang.


Here is what the problem looks like out in the wild. In VB.NET, the visualizer tricks you into thinking that the value of the GUID is Empty.

image

When you call .ToString on the GUID, there is clearly a GUID populated.

image

I ran the *same* code in C#, and it works perfectly. +1 for C#!

image

Microsoft left a bug in the two most recent versions of Visual Studio? Not really.
When I found that the GUID shows up correctly when you call the .ToString method, I realized that the GUID is
populated, it just doesn't appear correctly in the debugger. This led me to believe that there must be something else
going on. I got out Reflector to take a look at the System.Guid class and found the problem.
As it turns out, the System.Guid class contains a single read-only property named Empty. Because the Empty property
shows up in the debugger visualizer as the only member, it has the effect of making you think that the value is empty.
To make matters worse, the Empty property returns an empty (all zeros) instance of a System.Guid class. The result,
when you drill into the System.Guid variable: you get a never-ending tree of what appears to be Empty GUIDs.

image

We can't blame the visualizer specifically. The word Empty is misleading and leads developers to believe that a GUID
variable is empty - the "not used" type of empty.
You can work around this problem by downloading a visualizer for GUIDs (from here and here).  Both of these
visualizers give you the option to open a window in the debugger displaying the GUID's value. A little cumbersome.

 

Here's how you can create an even better GUID debugger visualizer on your own.
Create a VB.NET class library. If you are using Visual Studio 2008, make sure to target the .NET 2.0 framework. Create
a new class and put the code below into the class file. The class and project names do not matter.

<assembly: system.diagnostics.debuggerdisplay(="" {tostring}="" ,="" 
target:="GetType(System.Guid))">
Public Class Visualizer_GUID
    '---- no implementation necessary
End Class

Compile the code and put the output in your "My Documents\Visual Studio 2008\Visualizers" and/or "My
Documents\Visual Studio 2005\Visualizers" directories. Crank up another Visual Studio project, create a GUID, and hover over it in debug mode. Here's what you will see.

image

Now that's much better.
You can download my project and compiled class library here (11.3K).

 


TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a01053602891a970c010536789308970b

18 May 2009

Working with a CheckBoxList

/******************************************************************************
 
 Q10028 - C#: Working with a CheckBoxList

 Article ID: Q10028
 Created Date: 5/7/2007
 Last Modified: 5/7/2007
 Author: Dale
 Original URL: http://www.geekycodesamples.com/article.aspx?id=10028
 
******************************************************************************/




// How to populate a CheckBoxList from a DataTable
private void PopulateCheckboxList(CheckBoxList chkList)
{
   ProdConfigSupport pcs = new ProdConfigSupport();

   DataTable dt = pcs.GetProductList();

   if (dt != null && dt.Rows.Count > 0)
 {
       chkList.DataSource = dt;
       chkList.DataTextField = "ProductDescription";
       chkList.DataValueField = "ProductID";
       chkList.DataBind();
       chkList.Visible = true;
   }
 else
 {
       chkList.Visible = false;
   }
}




// How to iterate through a checkbox list. In this case we are
// capturing the ‘value’ of any Checkboxes that were selected
// and packaging them into an ArrayList
ArrayList arySelectedProducts = new ArrayList();

foreach (ListItem itm in chkBoxListProducts.Items)
{
   if (itm.Selected)
 {
       arySelectedProducts.Add(itm.Value);
   }
}




// How to programmatically ‘check’ the previously-checked selections
// of a CheckBoxList. In this case, the method
// GetPreviouslySelectedProducts() populates an ArrayList called
// arySelectedProducts that contains a list of the ProductID’s
// that should be ‘checked’
ArrayList arySelectedProducts = GetPreviouslySelectedProducts();

foreach (ListItem itm in this. chkBoxListProducts.Items)
{
   if (arySelectedProducts.Contains(Convert.ToString(itm.Value)))
 {
       itm.Selected = true;
   }
}

Differences between IDataReader DbDataReader and SqlDataReader

The IDataReader is an Interface. This means that it only contains a signature of the class structure. Every class which implements this interface need to implement the same methods. In this way you can call any class and be sure that they all support the same functionality.

As MSDN states in the IDataReader, this interface provides a means of reading one or more forward-only streams of result sets obtained by executing a command at a data source, and is implemented by .NET Framework data providers that access relational databases. If you search for the members of the IDataReader, you will see that properties like e.g. IsClosed and RecordsAffected and also methods like e.g. Close, NextResult and Read should be implemented when a class derives from from the IDataReader interface.

The DBDataReader is a class which implements this interface. According to msdn reads a forward-only stream of rows from a data source. If you look at the signature of the DbDataReader class,

Public Abstract Class DbDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable 
you will notice that the DbDataReader implements the IDataReader interface. You can be sure that this class will have all methods as described in the IDataReader members. But there is more. The DbDataReader class is a abstract class, which means that you can not create an object of this class (can not be instantiated). This is only intended to use as a base class for other classes. But is does implement already some functionality. Something which can not be done in an interface.

You can find on msdn the derived classes of the DbDataReader Class. You will find there the e.g. SqlDataReader class.

The SqlDataReader class inherits from the DbDataClass and adds some (Microsoft) SQL-server specific code. So, the SqlDataReader is a class which provides a way of reading a forward-only stream of rows from a (Microsoft) SQLServer database.

When to use: it depends on the situation, but most of the time you will use the SqlDataReader Class. If you write your own dataobject you can interit from the abstract DbDataReader class. Or if you want to implement all code yourself and only need the functionanlity of the IDataReader interface, you use this one.It is ofcourse possible to cast an object back to the interface if you don't know the exact type, but you do know that the interface is implemented. But that would take us a bit to far at this very moment.